code wiki / (root) / nx_doc_func_scan.nx

nx_doc_func_scan.nx source

↩ module page · 174 lines · 7794 B

1// nx_doc_func_scan.nx -- bits-up function-pattern extraction. 2// 3// Substrate-canonical building block for replacing bash + grep + 4// awk in audit primitives. Extracts `func NAME(` patterns from 5// NishiLang source content, returns iterator results compatible 6// with nx_doc_extract_link_target's pattern. 7// 8// Per [[feedback-bits-up-shell-replace-linux-brother-tools]]: 9// substrate's audit infrastructure stops depending on Linux-brother 10// tools for its own self-audit. bench/nx_audit_within_file_func_ 11// duplicates.sh (bash) is INTERIM; bits-up nx_audit_func_duplicates 12// composes THIS primitive. 13// 14// **Quadrant:** REFERENCE (Diátaxis) 15// **Topic Type:** REFERENCE (DITA) 16// **Status:** DRAFT (first stone; bits-up substitute for bash audit 17// detector) 18// **Trust State:** WRITTEN_UNTESTED 19// **Competitive State:** UNCLASSIFIED (no benchmark vs grep+awk 20// substrate-internally yet) 21 22import "nx_syscalls.nx" 23import "nx_string_ops.nx" 24 25// ===== Scan result struct ========================================= 26// 27// Same shape as NxLinkScanResult in nx_doc_xref for iterator 28// composition consistency. 29 30struct NxFuncScanResult { 31 next_offset: i64, // byte offset past the matched 'func' decl 32 // (for caller's next-iteration start); -1 if 33 // no more matches found 34 n_name: i64, // length of extracted function name 35 // (-1 if out_cap exceeded; 0 if no match) 36} 37 38// Find next `func NAME(` declaration at column 0 starting at offset 39// start_at in content. NAME is alphanumeric + underscore. Requires 40// the line to END WITH `{` so forward declarations (`func foo;`) 41// don't match. 42// 43// Substrate-honest: pattern is the same one bash uses 44// (^func[[:space:]]+NAME[[:space:]]*\(.*\{[[:space:]]*$). 45// 46// Writes NAME to out_name (NUL-terminated); writes 47// next_offset + n_name to out_result. 48// 49// Composes nx_str_index_of (byte search) for fast `func` lookup. 50 51func nx_doc_func_scan_next( 52 content: *u8, n_content: i64, 53 start_at: i64, 54 out_name: *u8, out_cap: i64, 55 out_result: *NxFuncScanResult 56) { 57 out_result.next_offset = -1 58 out_result.n_name = 0 59 if start_at < 0 { return } 60 if start_at >= n_content { return } 61 62 // Scan for line-starts; check if the line begins with 'func ' (5 bytes). 63 var i: i64 = start_at 64 // If start_at != 0, we're mid-content; advance to next line start. 65 if i > 0 { 66 if content[i - 1] != 10 { 67 // Find next newline. 68 var scan_nl: i64 = 1 69 while scan_nl == 1 { 70 if i >= n_content { scan_nl = 0; continue } 71 if content[i] == 10 { 72 i = i + 1 73 scan_nl = 0 74 continue 75 } 76 i = i + 1 77 } 78 } 79 } 80 81 // Iterate line-by-line looking for 'func NAME(...) {' at column 0. 82 var scan: i64 = 1 83 while scan == 1 { 84 if i >= n_content { scan = 0; continue } 85 86 // Find end of this line. 87 var line_end: i64 = i 88 var find_end: i64 = 1 89 while find_end == 1 { 90 if line_end >= n_content { find_end = 0; continue } 91 if content[line_end] == 10 { find_end = 0; continue } 92 line_end = line_end + 1 93 } 94 95 // Check if line starts with 'func ' (102 117 110 99 32). 96 let line_len: i64 = line_end - i 97 if line_len >= 6 { 98 if content[i] == 102 { 99 if content[i + 1] == 117 { 100 if content[i + 2] == 110 { 101 if content[i + 3] == 99 { 102 if content[i + 4] == 32 { 103 // Check the line ends with '{' (possibly 104 // followed by whitespace). 105 var le: i64 = line_end - 1 106 var trim: i64 = 1 107 while trim == 1 { 108 if le <= i { trim = 0; continue } 109 if content[le] != 32 { trim = 0; continue } 110 if content[le] == 9 { trim = 0; continue } 111 le = le - 1 112 } 113 if content[le] == 123 { // '{' 114 // Extract NAME from after 'func '. 115 let name_start: i64 = i + 5 116 var name_end: i64 = name_start 117 var scan_name: i64 = 1 118 while scan_name == 1 { 119 if name_end >= line_end { 120 scan_name = 0 121 continue 122 } 123 let c: i64 = content[name_end] as i64 124 var is_id: i64 = 0 125 if c >= 97 { if c <= 122 { is_id = 1 } } 126 if c >= 65 { if c <= 90 { is_id = 1 } } 127 if c >= 48 { if c <= 57 { is_id = 1 } } 128 if c == 95 { is_id = 1 } 129 if is_id == 0 { scan_name = 0; continue } 130 name_end = name_end + 1 131 } 132 let name_len: i64 = name_end - name_start 133 if name_len > 0 { 134 // Verify next char is '(' (after optional ws). 135 var p: i64 = name_end 136 var skip_ws: i64 = 1 137 while skip_ws == 1 { 138 if p >= line_end { skip_ws = 0; continue } 139 if content[p] != 32 { if content[p] != 9 { skip_ws = 0; continue } } 140 p = p + 1 141 } 142 if p < line_end { 143 if content[p] == 40 { // '(' 144 // Match! Write to out_name. 145 if name_len + 1 > out_cap { 146 out_result.n_name = -1 147 out_result.next_offset = line_end + 1 148 return 149 } 150 var k: i64 = 0 151 while k < name_len { 152 out_name[k] = content[name_start + k] 153 k = k + 1 154 } 155 out_name[name_len] = 0 156 out_result.n_name = name_len 157 out_result.next_offset = line_end + 1 158 return 159 } 160 } 161 } 162 } 163 } 164 } 165 } 166 } 167 } 168 } 169 170 // Advance to next line. 171 if line_end >= n_content { scan = 0; continue } 172 i = line_end + 1 173 } 174}