code wiki / (root) / nx_glob.nx

nx_glob.nx source

↩ module page · 221 lines · 8109 B

1// nx_glob.nx -- POSIX-style glob pattern matching. 2// 3// Used by toolchain CLI wrappers that take "*.nx", "tests/*.s", 4// "runtime/nx_dwarf_*.nx" arguments. Today each tool that wants 5// glob support either calls out to a shell or implements its own 6// matcher. This module provides a single canonical matcher. 7// 8// Supported syntax (POSIX subset): 9// * match zero or more chars (NEVER crosses '/') 10// ? match exactly one char 11// [abc] character class 12// [a-z] character range 13// [^abc] negated class 14// \\x literal escape 15// 16// Out of scope: 17// ** (recursive wildcard) 18// {a,b,c} brace expansion 19// ~ home-dir expansion 20// 21// Matcher is recursive (textbook NFA-style); for the pattern lengths 22// real CLIs use (< 100 chars) the depth is fine. 23 24// nx_safety_envelope: 25// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 26// sil_target: SIL1 27// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 28// verdict: NOT_YET_EVALUATED 29 30import "syscalls.nx" 31 32// Match a single character against a `[...]` class starting at 33// pat[i] (which is `[`). Returns the new pat index AFTER the 34// closing `]`, with `*matched_out` set to 1 if the class matched. 35// Returns -1 on malformed class (no closing `]`). 36func nx_glob_match_class(pat: *u8, i: i64, ch: i64, matched_out: *i64) -> i64 { 37 var k: i64 = i + 1 38 var negate: i64 = 0 39 if pat[k] == 0x5E { negate = 1; k = k + 1 } // '^' 40 var matched: i64 = 0 41 var done: i64 = 0 42 while done == 0 { 43 let c: i64 = pat[k] 44 if c == 0 { return -1 } 45 if c == 0x5D { done = 1 } 46 else { 47 // Range "x-y"? 48 if pat[k + 1] == 0x2D { 49 if pat[k + 2] != 0 { 50 if pat[k + 2] != 0x5D { 51 if ch >= c { 52 if ch <= pat[k + 2] { matched = 1 } 53 } 54 k = k + 3 55 } else { 56 if c == ch { matched = 1 } 57 k = k + 1 58 } 59 } else { 60 if c == ch { matched = 1 } 61 k = k + 1 62 } 63 } else { 64 if c == ch { matched = 1 } 65 k = k + 1 66 } 67 } 68 } 69 if negate == 1 { 70 if matched == 1 { *matched_out = 0 } 71 else { *matched_out = 1 } 72 } else { 73 *matched_out = matched 74 } 75 return k + 1 76} 77 78// Match `text` (NUL-terminated) against `pat` (NUL-terminated). 79// Returns 1 on full match, 0 otherwise. 80func nx_glob_match(pat: *u8, text: *u8) -> i64 { 81 var p: i64 = 0 82 var t: i64 = 0 83 var done: i64 = 0 84 var result: i64 = 0 85 while done == 0 { 86 let pc: i64 = pat[p] 87 let tc: i64 = text[t] 88 if pc == 0 { 89 if tc == 0 { result = 1 } 90 done = 1 91 } else { 92 if pc == 0x2A { 93 // '*' -- backtrack with successive prefixes. '*' must 94 // not cross '/'. Try each suffix of text until one 95 // matches the rest of pat. 96 let rest: *u8 = (((pat as i64) + p + 1) as *u8) 97 var k: i64 = t 98 var inner_done: i64 = 0 99 while inner_done == 0 { 100 let tail: *u8 = (((text as i64) + k) as *u8) 101 if nx_glob_match(rest, tail) == 1 { 102 result = 1 103 inner_done = 1 104 done = 1 105 } else { 106 if text[k] == 0 { inner_done = 1; done = 1 } 107 else { 108 if text[k] == 0x2F { inner_done = 1; done = 1 } 109 else { k = k + 1 } 110 } 111 } 112 } 113 } else { 114 if tc == 0 { 115 done = 1 116 } else { 117 if pc == 0x3F { 118 // '?' matches any single char (but not /). 119 if tc == 0x2F { done = 1 } 120 else { p = p + 1; t = t + 1 } 121 } else { 122 if pc == 0x5C { 123 // Escape: pat[p+1] is literal. 124 if pat[p + 1] == 0 { done = 1 } 125 else { 126 if pat[p + 1] != tc { done = 1 } 127 else { p = p + 2; t = t + 1 } 128 } 129 } else { 130 if pc == 0x5B { 131 let m_raw: *u8 = sys_mmap(8) 132 let m: *i64 = m_raw as *i64 133 let new_p: i64 = nx_glob_match_class(pat, p, tc, m) 134 if new_p < 0 { done = 1 } 135 else { 136 if *m == 1 { p = new_p; t = t + 1 } 137 else { done = 1 } 138 } 139 } else { 140 if pc != tc { done = 1 } 141 else { p = p + 1; t = t + 1 } 142 } 143 } 144 } 145 } 146 } 147 } 148 } 149 return result 150} 151 152// ---- self-test --------------------------------------------------- 153 154func main() -> i64 { 155 let p1: *u8 = sys_mmap(16) 156 let t1: *u8 = sys_mmap(32) 157 158 // Plain literal 159 p1[0] = 0x66; p1[1] = 0x6F; p1[2] = 0x6F; p1[3] = 0 160 t1[0] = 0x66; t1[1] = 0x6F; t1[2] = 0x6F; t1[3] = 0 161 if nx_glob_match(p1, t1) != 1 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 162 163 // Mismatch 164 t1[2] = 0x78 165 if nx_glob_match(p1, t1) != 0 { return __syscall(93, 2, 0, 0, 0, 0, 0) } 166 167 // * -- zero-or-more 168 let p2: *u8 = sys_mmap(8) 169 p2[0] = 0x66; p2[1] = 0x2A; p2[2] = 0 170 let t2: *u8 = sys_mmap(8) 171 t2[0] = 0x66; t2[1] = 0x6F; t2[2] = 0x6F; t2[3] = 0 172 if nx_glob_match(p2, t2) != 1 { return __syscall(93, 3, 0, 0, 0, 0, 0) } 173 174 // *.nx 175 let p3: *u8 = sys_mmap(8) 176 p3[0] = 0x2A; p3[1] = 0x2E; p3[2] = 0x6E; p3[3] = 0x78; p3[4] = 0 177 let t3: *u8 = sys_mmap(16) 178 t3[0] = 0x6E; t3[1] = 0x78; t3[2] = 0x5F; t3[3] = 0x68; t3[4] = 0x61 179 t3[5] = 0x73; t3[6] = 0x68; t3[7] = 0x2E; t3[8] = 0x6E; t3[9] = 0x78; t3[10] = 0 180 if nx_glob_match(p3, t3) != 1 { return __syscall(93, 4, 0, 0, 0, 0, 0) } 181 182 // ? matches one char 183 let p4: *u8 = sys_mmap(8) 184 p4[0] = 0x66; p4[1] = 0x3F; p4[2] = 0x6F; p4[3] = 0 185 let t4: *u8 = sys_mmap(8) 186 t4[0] = 0x66; t4[1] = 0x6F; t4[2] = 0x6F; t4[3] = 0 187 if nx_glob_match(p4, t4) != 1 { return __syscall(93, 5, 0, 0, 0, 0, 0) } 188 189 // ? does NOT match zero chars 190 let t4b: *u8 = sys_mmap(8) 191 t4b[0] = 0x66; t4b[1] = 0x6F; t4b[2] = 0 192 if nx_glob_match(p4, t4b) != 0 { return __syscall(93, 6, 0, 0, 0, 0, 0) } 193 194 // [abc] class 195 let p5: *u8 = sys_mmap(8) 196 p5[0] = 0x5B; p5[1] = 0x61; p5[2] = 0x62; p5[3] = 0x63; p5[4] = 0x5D; p5[5] = 0 197 let t5: *u8 = sys_mmap(8) 198 t5[0] = 0x62; t5[1] = 0 199 if nx_glob_match(p5, t5) != 1 { return __syscall(93, 7, 0, 0, 0, 0, 0) } 200 t5[0] = 0x64 201 if nx_glob_match(p5, t5) != 0 { return __syscall(93, 8, 0, 0, 0, 0, 0) } 202 203 // [a-z] range 204 let p6: *u8 = sys_mmap(8) 205 p6[0] = 0x5B; p6[1] = 0x61; p6[2] = 0x2D; p6[3] = 0x7A; p6[4] = 0x5D; p6[5] = 0 206 let t6: *u8 = sys_mmap(8) 207 t6[0] = 0x6A; t6[1] = 0 208 if nx_glob_match(p6, t6) != 1 { return __syscall(93, 9, 0, 0, 0, 0, 0) } 209 t6[0] = 0x41 210 if nx_glob_match(p6, t6) != 0 { return __syscall(93, 10, 0, 0, 0, 0, 0) } 211 212 // * does not cross '/' 213 let p7: *u8 = sys_mmap(8) 214 p7[0] = 0x2A; p7[1] = 0x2E; p7[2] = 0x6E; p7[3] = 0x78; p7[4] = 0 215 let t7: *u8 = sys_mmap(16) 216 t7[0] = 0x64; t7[1] = 0x69; t7[2] = 0x72; t7[3] = 0x2F 217 t7[4] = 0x66; t7[5] = 0x2E; t7[6] = 0x6E; t7[7] = 0x78; t7[8] = 0 218 if nx_glob_match(p7, t7) != 0 { return __syscall(93, 11, 0, 0, 0, 0, 0) } 219 220 return 0 221}