code wiki / _hdl_build / nx_doc_kwscan.nx

nx_doc_kwscan.nx source

↩ module page · 125 lines · 6226 B

1// nx_doc_kwscan.nx -- DOCTOR reserved-keyword diagnosis (the reserved-kw sub-case of empty-.s, the sibling of the 2// let-reassign fixer). When dah_resolve returns DAH_NOFIX on an EMPTYS class, THIS pinpoints WHY: it finds an 3// identifier that collides with a NishiLang reserved keyword used in a position the grammar requires an identifier 4// (a let/var/const/static/func declared NAME, or a parameter NAME), and reports the keyword + byte offset + a safe 5// suggested rename. READ-ONLY BY CONSTRUCTION: it does NOT auto-rewrite -- a blind keyword rename could clobber a 6// legitimate keyword USE (a real `match {}` statement, a `return`, a `while`), so detection + suggestion is the 7// fail-safe boundary (the human/Doctor applies the rename with scope knowledge). The reserved-keyword set is the 8// AUTHORITATIVE lexer list (runtime/lex.nx keyword_lookup, 22 kw) -- NOT folklore: `type`/`import` are NOT lexer 9// keywords, so they are valid identifiers and are deliberately NOT flagged. license_tier: ORIGINAL 10import "nx_syscalls.nx" 11import "nx_doc_heal_let.nx" 12 13// whole-word compare: 1 iff t[0..len) equals the nul-terminated literal `lit` exactly. 14func kw_eq(t: *u8, len: i64, lit: *u8) -> i64 { 15 var L: i64 = 0; while lit[L] != (0 as u8) { L = L + 1 } 16 if L != len { return 0 } 17 var k: i64 = 0 18 while k < len { if t[k] != lit[k] { return 0 } k = k + 1 } 19 return 1 20} 21// the 22 reserved keywords, mirrored verbatim from runtime/lex.nx keyword_lookup (the SSOT). 1 if t[0..len) is one. 22func kw_is_reserved(t: *u8, len: i64) -> i64 { 23 if kw_eq(t,len,"func" as *u8)==1 { return 1 } 24 if kw_eq(t,len,"let" as *u8)==1 { return 1 } 25 if kw_eq(t,len,"var" as *u8)==1 { return 1 } 26 if kw_eq(t,len,"const" as *u8)==1 { return 1 } 27 if kw_eq(t,len,"static" as *u8)==1 { return 1 } 28 if kw_eq(t,len,"if" as *u8)==1 { return 1 } 29 if kw_eq(t,len,"else" as *u8)==1 { return 1 } 30 if kw_eq(t,len,"while" as *u8)==1 { return 1 } 31 if kw_eq(t,len,"loop" as *u8)==1 { return 1 } 32 if kw_eq(t,len,"for" as *u8)==1 { return 1 } 33 if kw_eq(t,len,"in" as *u8)==1 { return 1 } 34 if kw_eq(t,len,"break" as *u8)==1 { return 1 } 35 if kw_eq(t,len,"continue" as *u8)==1 { return 1 } 36 if kw_eq(t,len,"return" as *u8)==1 { return 1 } 37 if kw_eq(t,len,"true" as *u8)==1 { return 1 } 38 if kw_eq(t,len,"false" as *u8)==1 { return 1 } 39 if kw_eq(t,len,"struct" as *u8)==1 { return 1 } 40 if kw_eq(t,len,"enum" as *u8)==1 { return 1 } 41 if kw_eq(t,len,"match" as *u8)==1 { return 1 } 42 if kw_eq(t,len,"comptime" as *u8)==1 { return 1 } 43 if kw_eq(t,len,"extern" as *u8)==1 { return 1 } 44 if kw_eq(t,len,"as" as *u8)==1 { return 1 } 45 return 0 46} 47// a declaration keyword whose NEXT token must be an identifier (the declared name). 48func kw_is_decl(t: *u8, len: i64) -> i64 { 49 if kw_eq(t,len,"func" as *u8)==1 { return 1 } 50 if kw_eq(t,len,"let" as *u8)==1 { return 1 } 51 if kw_eq(t,len,"var" as *u8)==1 { return 1 } 52 if kw_eq(t,len,"const" as *u8)==1 { return 1 } 53 if kw_eq(t,len,"static" as *u8)==1 { return 1 } 54 return 0 55} 56// nearest non-space/tab char code strictly before i (or -1 at start). 57func kws_prev_nonspace(src: *u8, i: i64) -> i64 { 58 var p: i64 = i - 1 59 while p >= 0 { if src[p]==(32 as u8) { p=p-1 } else { if src[p]==(9 as u8) { p=p-1 } else { return src[p] as i64 } } } 60 return 0-1 61} 62// nearest non-space/tab char code at/after j within n (or -1 at end). 63func kws_next_nonspace(src: *u8, n: i64, j: i64) -> i64 { 64 var p: i64 = j 65 while p < n { if src[p]==(32 as u8) { p=p+1 } else { if src[p]==(9 as u8) { p=p+1 } else { return src[p] as i64 } } } 66 return 0-1 67} 68// scan for the FIRST reserved-keyword-as-identifier at a declaration or parameter site. On a hit: copy the keyword 69// into outkw (nul-term), write its byte offset to outpos[0], return 1. No hit: return 0 (outs untouched). 70func kws_scan(src: *u8, n: i64, outkw: *u8, outpos: *i64) -> i64 { 71 let nb: *u8 = sys_mmap(256) 72 let nb2: *u8 = sys_mmap(256) 73 var i: i64 = 0 74 while i < n { 75 var isstart: i64 = 0 76 if dhl_isident(src[i] as i64) == 1 { 77 if i == 0 { isstart = 1 } else { if dhl_isident(src[i-1] as i64) == 0 { isstart = 1 } } 78 } 79 if isstart == 1 { 80 let nl: i64 = dhl_name_at(src, n, i, nb) 81 if kw_is_decl(nb, nl) == 1 { 82 // the declared NAME (next token) must be an identifier; a reserved kw there = the error 83 var k: i64 = i + nl 84 var sk: i64 = 0 85 while sk == 0 { if k < n { if src[k]==(32 as u8){k=k+1} else { if src[k]==(9 as u8){k=k+1} else {sk=1} } } else {sk=1} } 86 if k < n { if dhl_isident(src[k] as i64) == 1 { 87 let nl2: i64 = dhl_name_at(src, n, k, nb2) 88 if kw_is_reserved(nb2, nl2) == 1 { 89 var c: i64=0; while c<nl2 { outkw[c]=nb2[c]; c=c+1 } outkw[nl2]=0 as u8 90 outpos[0] = k 91 return 1 92 } 93 } } 94 } else { 95 // a parameter/field NAME position: ( NAME : ... or , NAME : ... 96 if kw_is_reserved(nb, nl) == 1 { 97 let pc: i64 = kws_prev_nonspace(src, i) 98 let nc: i64 = kws_next_nonspace(src, n, i + nl) 99 if nc == 58 { if pc == 40 { 100 var c: i64=0; while c<nl { outkw[c]=nb[c]; c=c+1 } outkw[nl]=0 as u8 101 outpos[0] = i 102 return 1 103 } } 104 if nc == 58 { if pc == 44 { 105 var c: i64=0; while c<nl { outkw[c]=nb[c]; c=c+1 } outkw[nl]=0 as u8 106 outpos[0] = i 107 return 1 108 } } 109 } 110 } 111 i = i + nl 112 } else { 113 i = i + 1 114 } 115 } 116 return 0 117} 118// suggested safe rename: the keyword with a trailing underscore (e.g. match -> match_). nul-terminated; returns len. 119func kws_suggest(kw: *u8, out: *u8) -> i64 { 120 var n: i64 = 0 121 while kw[n] != (0 as u8) { out[n] = kw[n]; n = n + 1 } 122 out[n] = 95 as u8 123 out[n+1] = 0 as u8 124 return n + 1 125}