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}