Skip to content

Commit cc3b0e0

Browse files
committed
feat(string-utils): add multi-base parsing and digit validation helpers
- Add NAVIsBinaryDigit() and NAVIsOctalDigit() public helper functions - Add NAVIsValidDigitForBase() and NAVCharToDigit() private helpers - Update NAVParseInteger() to support binary, octal, and hexadecimal - Update NAVParseSignedInteger() to support binary, octal, and hexadecimal - Update NAVParseLong() to support binary, octal, and hexadecimal - Update NAVParseSignedLong() to support binary, octal, and hexadecimal - Support prefixes: 0b/0B (binary), 0o/0O (octal), 0x/0X/$ (hex)
1 parent 8519d84 commit cc3b0e0

File tree

8 files changed

+1039
-162
lines changed

8 files changed

+1039
-162
lines changed

StringUtils/NAVFoundation.StringUtils.axi

Lines changed: 511 additions & 135 deletions
Large diffs are not rendered by default.

StringUtils/README.md

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,35 +208,54 @@ result = NAVStringScreamKebabCase('hello world') // Returns 'HELLO-WORLD'
208208

209209
```netlinx
210210
// Parse unsigned integer (0-65535)
211+
// Supports decimal, hexadecimal (0x or $), binary (0b), and octal (0o)
211212
stack_var integer value
212213
stack_var char success
213214
success = NAVParseInteger('12345', value) // Returns true, value = 12345
215+
success = NAVParseInteger('0xFF', value) // Returns true, value = 255 (hex)
216+
success = NAVParseInteger('$FFFF', value) // Returns true, value = 65535 (hex with $)
217+
success = NAVParseInteger('0b1111', value) // Returns true, value = 15 (binary)
218+
success = NAVParseInteger('0o777', value) // Returns true, value = 511 (octal)
214219
success = NAVParseInteger('99999', value) // Returns false (out of range)
215220
success = NAVParseInteger('-100', value) // Returns false (negative)
216221
success = NAVParseInteger('Volume=50', value) // Returns true, value = 50 (extracts first number)
217222
success = NAVParseInteger(' 42 99 ', value) // Returns true, value = 42 (stops at space)
218223
219224
// Parse signed integer (-32768 to 32767)
225+
// Supports decimal, hexadecimal (0x or $), binary (0b), and octal (0o)
220226
stack_var sinteger svalue
221227
success = NAVParseSignedInteger('-12345', svalue) // Returns true, svalue = -12345
228+
success = NAVParseSignedInteger('0x7FFF', svalue) // Returns true, svalue = 32767 (max)
229+
success = NAVParseSignedInteger('-0x8000', svalue) // Returns true, svalue = -32768 (min)
230+
success = NAVParseSignedInteger('0b1010', svalue) // Returns true, svalue = 10 (binary)
231+
success = NAVParseSignedInteger('-0o77', svalue) // Returns true, svalue = -63 (octal)
222232
success = NAVParseSignedInteger('50000', svalue) // Returns false (out of range)
223233
success = NAVParseSignedInteger('Offset=-100', svalue) // Returns true, svalue = -100
224234
success = NAVParseSignedInteger(' -10 20 ', svalue) // Returns true, svalue = -10
225235
226236
// Parse unsigned long (0-4294967295)
237+
// Supports decimal, hexadecimal (0x or $), binary (0b), and octal (0o)
227238
stack_var long lvalue
228239
success = NAVParseLong('1234567890', lvalue) // Returns true, lvalue = 1234567890
229240
success = NAVParseLong('4294967295', lvalue) // Returns true (max LONG value)
241+
success = NAVParseLong('0xFFFFFFFF', lvalue) // Returns true, lvalue = 4294967295 (hex)
242+
success = NAVParseLong('0b11111111', lvalue) // Returns true, lvalue = 255 (binary)
243+
success = NAVParseLong('0o7777', lvalue) // Returns true, lvalue = 4095 (octal)
230244
success = NAVParseLong('5000000000', lvalue) // Returns false (overflow)
231245
success = NAVParseLong('-1000', lvalue) // Returns false (negative)
232246
success = NAVParseLong(' 100 200 ', lvalue) // Returns true, lvalue = 100
233247
234248
// Parse signed long (-2147483648 to 2147483647)
249+
// Supports decimal, hexadecimal (0x or $), binary (0b), and octal (0o)
235250
stack_var slong slvalue
236251
success = NAVParseSignedLong('-1234567890', slvalue) // Returns true, slvalue = -1234567890
237252
success = NAVParseSignedLong('2147483647', slvalue) // Returns true (max SLONG)
253+
success = NAVParseSignedLong('0x7FFFFFFF', slvalue) // Returns true, slvalue = 2147483647 (hex)
254+
success = NAVParseSignedLong('-0x80000000', slvalue) // Returns true, slvalue = -2147483648 (min)
255+
success = NAVParseSignedLong('0b11111111', slvalue) // Returns true, slvalue = 255 (binary)
256+
success = NAVParseSignedLong('-0o777', slvalue) // Returns true, slvalue = -511 (octal)
238257
success = NAVParseSignedLong('3000000000', slvalue) // Returns false (overflow)
239-
success = NAVParseSignedLong('xyz-123', slvalue) // Returns true, slvalue = -123 (extracts number)
258+
success = NAVParseSignedLong('xyz-123', slvalue) // Returns true, slvalue = -123 (extracts number)
240259
success = NAVParseSignedLong(' -100 200 ', slvalue) // Returns true, slvalue = -100
241260
242261
// Parse floating-point number
@@ -267,9 +286,13 @@ success = NAVParseBoolean('', bvalue) // Returns false (empty string)
267286
- Parsing stops at the first space or non-digit character after the number (matching ATOI/ATOF behavior)
268287
- Example: `' 10 20 '` parses as `10`, not `20` or `1020`
269288
- The functions validate ranges and return `false` if the value is out of bounds
270-
- `NAVParseInteger` and `NAVParseSignedInteger` use ATOI internally
271-
- `NAVParseLong` and `NAVParseSignedLong` use manual digit-by-digit parsing for full range support and overflow detection
272-
- `NAVParseFloat` uses ATOF internally
289+
- **Multi-base Support**: `NAVParseInteger`, `NAVParseSignedInteger`, `NAVParseLong`, and `NAVParseSignedLong` automatically detect number bases:
290+
- **Hexadecimal**: `0x` or `0X` prefix (e.g., `'0xFF'`, `'0x1A2B'`) or `$` prefix (e.g., `'$FF'`, `'$1A2B'`)
291+
- **Binary**: `0b` or `0B` prefix (e.g., `'0b1010'`, `'0b11111111'`)
292+
- **Octal**: `0o` or `0O` prefix (e.g., `'0o777'`, `'0o123'`)
293+
- **Decimal**: No prefix (e.g., `'123'`, `'-456'`)
294+
- `NAVParseLong` and `NAVParseSignedLong` use manual digit-by-digit parsing for full range support and precise overflow detection
295+
- `NAVParseFloat` uses ATOF internally and only supports decimal notation
273296
- `NAVParseBoolean` performs case-insensitive matching of valid boolean strings:
274297
- **True values**: `'1'`, `'true'`, `'yes'`, `'on'`
275298
- **False values**: `'0'`, `'false'`, `'no'`, `'off'`
@@ -392,6 +415,23 @@ if (success) {
392415
send_command dvAudioDevice, "'VOLUME-', itoa(volume)"
393416
}
394417
418+
// Parse hexadecimal color values
419+
stack_var char colorStr[10]
420+
stack_var integer red, green, blue
421+
422+
colorStr = '0xFF'
423+
success = NAVParseInteger(colorStr, red) // red = 255
424+
425+
colorStr = '$A5'
426+
success = NAVParseInteger(colorStr, green) // green = 165
427+
428+
// Parse binary flags
429+
stack_var char flagsStr[20]
430+
stack_var integer flags
431+
432+
flagsStr = '0b11010110'
433+
success = NAVParseInteger(flagsStr, flags) // flags = 214
434+
395435
// Parse temperature with negative values
396436
stack_var char tempStr[20]
397437
stack_var sinteger temperature
@@ -402,6 +442,10 @@ if (success) {
402442
// temperature = -15, valid range -32768 to 32767
403443
}
404444
445+
// Parse hex values with sign
446+
stack_var sinteger signedHex
447+
success = NAVParseSignedInteger('-0x10', signedHex) // signedHex = -16
448+
405449
// Parse large numbers like timestamps
406450
stack_var char timestampStr[20]
407451
stack_var long timestamp
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
PROGRAM_NAME='NAVHextoiExploration.axi'
2+
3+
#include 'NAVFoundation.Core.axi'
4+
#include 'NAVFoundation.Testing.axi'
5+
6+
DEFINE_CONSTANT
7+
8+
constant char HEXTOI_TEST_INPUTS[][255] = {
9+
// Basic hex digits
10+
'FF', // 1: Basic hex - expect 255
11+
'1A', // 2: Basic hex - expect 26
12+
'0', // 3: Zero - expect 0
13+
'FFFF', // 4: Large hex - expect 65535
14+
15+
// With 0x prefix
16+
'0xFF', // 5: With 0x prefix
17+
'0X1A', // 6: With 0X prefix
18+
'0x0', // 7: Zero with prefix
19+
20+
// With $ prefix
21+
'$FF', // 8: With $ prefix
22+
'$1A', // 9: With $ prefix
23+
'$0', // 10: Zero with $ prefix
24+
25+
// With whitespace
26+
' FF', // 11: Leading whitespace
27+
'FF ', // 12: Trailing whitespace
28+
' 1A ', // 13: Both whitespace
29+
30+
// With negative sign
31+
'-FF', // 14: Negative hex (no prefix)
32+
'-0xFF', // 15: Negative with 0x prefix
33+
'-$FF', // 16: Negative with $ prefix
34+
35+
// With non-hex characters
36+
'GGG', // 17: No valid hex - expect 0
37+
'1G2', // 18: Invalid char in middle
38+
'xyz1A', // 19: Text prefix
39+
'1Axyz', // 20: Text suffix
40+
41+
// Mixed/edge cases
42+
'', // 21: Empty string - expect 0
43+
' ', // 22: Only whitespace - expect 0
44+
'0xGGG', // 23: Prefix but no valid hex
45+
'Value=FF', // 24: Text before hex
46+
'FF FF', // 25: Multiple hex with space
47+
'0xFF 0x10', // 26: Multiple hex with prefixes
48+
49+
// Case sensitivity
50+
'ff', // 27: Lowercase
51+
'Ff', // 28: Mixed case
52+
'0xff', // 29: Lowercase prefix
53+
54+
// Large values
55+
'FFFFFFFF', // 30: Max 32-bit - expect 4294967295
56+
'100000000', // 31: Overflow test
57+
58+
// Special patterns
59+
'0x', // 32: Prefix only
60+
'$', // 33: $ only
61+
'0x 10', // 34: Space after prefix
62+
'10 20 30' // 35: Multiple numbers with spaces
63+
}
64+
65+
constant long HEXTOI_EXPECTED_VALUES[35] = {
66+
255, // 1: 'FF'
67+
26, // 2: '1A'
68+
0, // 3: '0'
69+
65535, // 4: 'FFFF'
70+
0, // 5: '0xFF' - UNKNOWN (testing)
71+
0, // 6: '0X1A' - UNKNOWN (testing)
72+
0, // 7: '0x0' - UNKNOWN (testing)
73+
0, // 8: '$FF' - UNKNOWN (testing)
74+
0, // 9: '$1A' - UNKNOWN (testing)
75+
0, // 10: '$0' - UNKNOWN (testing)
76+
255, // 11: ' FF' - UNKNOWN (testing)
77+
255, // 12: 'FF ' - UNKNOWN (testing)
78+
26, // 13: ' 1A ' - UNKNOWN (testing)
79+
0, // 14: '-FF' - UNKNOWN (testing)
80+
0, // 15: '-0xFF' - UNKNOWN (testing)
81+
0, // 16: '-$FF' - UNKNOWN (testing)
82+
0, // 17: 'GGG' - expect 0
83+
1, // 18: '1G2' - UNKNOWN (testing)
84+
0, // 19: 'xyz1A' - UNKNOWN (testing)
85+
26, // 20: '1Axyz' - UNKNOWN (testing)
86+
0, // 21: '' - expect 0
87+
0, // 22: ' ' - expect 0
88+
0, // 23: '0xGGG' - UNKNOWN (testing)
89+
0, // 24: 'Value=FF' - UNKNOWN (testing)
90+
255, // 25: 'FF FF' - UNKNOWN (testing)
91+
255, // 26: '0xFF 0x10' - UNKNOWN (testing)
92+
255, // 27: 'ff' - lowercase
93+
255, // 28: 'Ff' - mixed
94+
0, // 29: '0xff' - UNKNOWN (testing)
95+
4294967295, // 30: 'FFFFFFFF' - max 32-bit
96+
0, // 31: '100000000' - UNKNOWN (testing)
97+
0, // 32: '0x' - expect 0
98+
0, // 33: '$' - expect 0
99+
0, // 34: '0x 10' - UNKNOWN (testing)
100+
16 // 35: '10 20 30' - UNKNOWN (testing)
101+
}
102+
103+
define_function TestHextoiExploration() {
104+
stack_var integer x
105+
stack_var long result
106+
107+
NAVLogTestSuiteStart('hextoi() Exploration')
108+
109+
NAVLog("'Testing hextoi() behavior with ', itoa(length_array(HEXTOI_TEST_INPUTS)), ' different inputs'")
110+
NAVLog("'Note: Many expected values are set to 0 because behavior is unknown - we are discovering it!'")
111+
NAVLog("'='")
112+
113+
for (x = 1; x <= length_array(HEXTOI_TEST_INPUTS); x++) {
114+
result = hextoi(HEXTOI_TEST_INPUTS[x])
115+
116+
// Log the result for analysis
117+
NAVLog("'Test #', itoa(x), ': Input=[', HEXTOI_TEST_INPUTS[x], '] => Result=', itoa(result), ' (Expected: ', itoa(HEXTOI_EXPECTED_VALUES[x]), ')'")
118+
119+
// We're not failing tests here - just observing behavior
120+
NAVLogTestPassed(x)
121+
}
122+
123+
NAVLogTestSuiteEnd('hextoi() Exploration')
124+
}

__tests__/include/string-utils/NAVParseInteger.axi

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,36 @@ constant char NAV_PARSE_INTEGER_TESTS[][255] = {
3232
'123.45', // 21: Decimal point (not integer format)
3333
{' ', $0D, $0A}, // 22: Whitespace characters (space, CR, LF)
3434
{' ', $0D, $0A, '1', '5', '0', $09}, // 23: Whitespace with number (space, CR, LF, '150', tab)
35-
' 10 20 ' // 24: Multiple numbers with spaces
35+
' 10 20 ', // 24: Multiple numbers with spaces
36+
37+
// Hexadecimal tests
38+
'0xFF', // 25: Hex with 0x prefix (255)
39+
'0XFF', // 26: Hex with 0X prefix (255)
40+
'0x10', // 27: Hex with 0x prefix (16)
41+
'0xFFFF', // 28: Hex max value (65535)
42+
'0x0', // 29: Hex zero
43+
'0xABCD', // 30: Hex mixed case (43981)
44+
45+
// Binary tests
46+
'0b1111', // 31: Binary (15)
47+
'0B1010', // 32: Binary uppercase (10)
48+
'0b11111111', // 33: Binary (255)
49+
'0b0', // 34: Binary zero
50+
'0b1111111111111111', // 35: Binary max (65535)
51+
52+
// Octal tests
53+
'0o77', // 36: Octal (63)
54+
'0O777', // 37: Octal uppercase (511)
55+
'0o177777', // 38: Octal max (65535)
56+
'0o0', // 39: Octal zero
57+
58+
// Invalid multi-base cases
59+
'0x10000', // 40: Hex overflow (65536)
60+
'0b10000000000000000', // 41: Binary overflow (65536)
61+
'0o200000', // 42: Octal overflow (65536)
62+
'0xGHI', // 43: Invalid hex digits
63+
'0b2', // 44: Invalid binary digit
64+
'0o8' // 45: Invalid octal digit
3665
}
3766

3867
constant char NAV_PARSE_INTEGER_EXPECTED_RESULT[] = {
@@ -45,7 +74,15 @@ constant char NAV_PARSE_INTEGER_EXPECTED_RESULT[] = {
4574
// Invalid (22)
4675
false,
4776
// Valid (23-24)
48-
true, true
77+
true, true,
78+
// Hexadecimal - Valid (25-30)
79+
true, true, true, true, true, true,
80+
// Binary - Valid (31-35)
81+
true, true, true, true, true,
82+
// Octal - Valid (36-39)
83+
true, true, true, true,
84+
// Invalid multi-base (40-45)
85+
false, false, false, false, false, false
4986
}
5087

5188
constant integer NAV_PARSE_INTEGER_EXPECTED_VALUES[] = {
@@ -63,7 +100,25 @@ constant integer NAV_PARSE_INTEGER_EXPECTED_VALUES[] = {
63100
123, // 20: 'xyz123' - ATOI extracts 123
64101
123, // 21: '123.45' - ATOI extracts 123
65102
150, // 23: Whitespace with number - ATOI extracts 150
66-
10 // 24: ' 10 20 ' - ATOI stops at space, returns 10
103+
10, // 24: ' 10 20 ' - ATOI stops at space, returns 10
104+
// Hexadecimal values
105+
255, // 25: '0xFF'
106+
255, // 26: '0XFF'
107+
16, // 27: '0x10'
108+
65535, // 28: '0xFFFF'
109+
0, // 29: '0x0'
110+
43981, // 30: '0xABCD'
111+
// Binary values
112+
15, // 31: '0b1111'
113+
10, // 32: '0B1010'
114+
255, // 33: '0b11111111'
115+
0, // 34: '0b0'
116+
65535, // 35: '0b1111111111111111'
117+
// Octal values
118+
63, // 36: '0o77'
119+
511, // 37: '0O777'
120+
65535, // 38: '0o177777'
121+
0 // 39: '0o0'
67122
}
68123

69124
define_function TestNAVParseInteger() {

0 commit comments

Comments
 (0)