code wiki / _hdl_build / nx_research_relation_clean.nx
nx_research_relation_clean.nx source
↩ module page · 180 lines · 7822 B
1// nx_research_relation_clean.nx -- Researcher EXTRACT, rung 3: CLAIM CLEANUP.
2// The measured noise from rung 2 (pronoun subjects, "[1]"/"^" citation markers,
3// "Wikipedia(R) trademark" boilerplate, junk) is fixed here -- by IMPROVING the
4// claims, not by stripping the feature. Inspectable + data-driven (stopword /
5// pronoun / boilerplate tables). nx_claim_clean(subj,obj -> clean) -> 1 good / 0 reject.
6// license_tier: ORIGINAL.
7
8import "nx_syscalls.nx"
9import "nx_research_extract.nx"
10
11func cl_is_alpha(c: u8) -> i64 {
12 if c >= (65 as u8) { if c <= (90 as u8) { return 1 } }
13 if c >= (97 as u8) { if c <= (122 as u8) { return 1 } }
14 return 0
15}
16func cl_is_alnum(c: u8) -> i64 {
17 if cl_is_alpha(c) == 1 { return 1 }
18 if c >= (48 as u8) { if c <= (57 as u8) { return 1 } }
19 return 0
20}
21func cl_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
22// a real entity subject is short; an over-long "subject" is a cue-mismatch/fragment (rung R3c)
23func cl_max_subj_words() -> i64 { return 6 }
24func cl_word_count(s: *u8) -> i64 {
25 var n: i64 = 0; var i: i64 = 0; var in_word: i64 = 0
26 while s[i] != (0 as u8) {
27 if s[i] == (32 as u8) { in_word = 0 } else { if in_word == 0 { n = n + 1; in_word = 1 } }
28 i = i + 1
29 }
30 return n
31}
32func cl_streq(a: *u8, b: *u8) -> i64 {
33 var i: i64 = 0
34 while i < 256 { if a[i] != b[i] { return 0 } if a[i] == (0 as u8) { return 1 } i = i + 1 }
35 return 1
36}
37func cl_lc(c: u8) -> u8 { if c >= (65 as u8) { if c <= (90 as u8) { return (c + 32) as u8 } } return c }
38// case-insensitive equality -- leading stopwords/pronouns are often Capitalized
39func cl_streq_ci(a: *u8, b: *u8) -> i64 {
40 var i: i64 = 0
41 while i < 256 { if cl_lc(a[i]) != cl_lc(b[i]) { return 0 } if a[i] == (0 as u8) { return 1 } i = i + 1 }
42 return 1
43}
44
45// first whitespace-delimited token of s into tok; returns token length
46func cl_first_token(s: *u8, tok: *u8) -> i64 {
47 var i: i64 = 0
48 while s[i] != (0 as u8) { if s[i] == (32 as u8) { break } tok[i] = s[i]; i = i + 1 }
49 tok[i] = 0 as u8
50 return i
51}
52
53// leading-stopword table (dropped from the front of a subject, repeatedly)
54func cl_is_stopword(t: *u8) -> i64 {
55 if cl_streq_ci(t,"the" as *u8) == 1 { return 1 }
56 if cl_streq_ci(t,"a" as *u8) == 1 { return 1 }
57 if cl_streq_ci(t,"an" as *u8) == 1 { return 1 }
58 if cl_streq_ci(t,"and" as *u8) == 1 { return 1 }
59 if cl_streq_ci(t,"but" as *u8) == 1 { return 1 }
60 if cl_streq_ci(t,"which" as *u8) == 1 { return 1 }
61 if cl_streq_ci(t,"that" as *u8) == 1 { return 1 }
62 if cl_streq_ci(t,"this" as *u8) == 1 { return 1 }
63 if cl_streq_ci(t,"these" as *u8) == 1 { return 1 }
64 if cl_streq_ci(t,"they" as *u8) == 1 { return 1 }
65 if cl_streq_ci(t,"what" as *u8) == 1 { return 1 }
66 return 0
67}
68// bare-pronoun subject -> reject entirely (no real entity)
69func cl_is_pronoun(t: *u8) -> i64 {
70 if cl_streq_ci(t,"it" as *u8) == 1 { return 1 }
71 if cl_streq_ci(t,"they" as *u8) == 1 { return 1 }
72 if cl_streq_ci(t,"which" as *u8) == 1 { return 1 }
73 if cl_streq_ci(t,"what" as *u8) == 1 { return 1 }
74 if cl_streq_ci(t,"this" as *u8) == 1 { return 1 }
75 if cl_streq_ci(t,"that" as *u8) == 1 { return 1 }
76 return 0
77}
78// boilerplate terms -> reject the claim
79func cl_boiler(s: *u8) -> i64 {
80 let n: i64 = cl_strlen(s)
81 if re_find(s, n, "Wikipedia" as *u8) >= 0 { return 1 }
82 if re_find(s, n, "trademark" as *u8) >= 0 { return 1 }
83 if re_find(s, n, "Foundation" as *u8) >= 0 { return 1 }
84 if re_find(s, n, "Retrieved" as *u8) >= 0 { return 1 }
85 if re_find(s, n, "ISBN" as *u8) >= 0 { return 1 }
86 if re_find(s, n, "edit]" as *u8) >= 0 { return 1 }
87 return 0
88}
89
90// shift out[] left, deleting the first token (+ following space)
91func cl_drop_first_token(out: *u8, tl: i64) -> i64 {
92 var src: i64 = tl
93 if out[src] == (32 as u8) { src = src + 1 }
94 var d: i64 = 0
95 while out[src] != (0 as u8) { out[d] = out[src]; d = d + 1; src = src + 1 }
96 out[d] = 0 as u8
97 return d
98}
99
100// copy src into out starting at first alphanumeric char; first drops a leading
101// "[..]" citation block, then leading markers/quotes/spaces -- but KEEPS leading
102// digits so "3D"/"2D" survive (the old version ate the "3" of "3D").
103func cl_copy_from_alpha(src: *u8, out: *u8) -> i64 {
104 var p: i64 = 0
105 if src[p] == (91 as u8) { // '[' -> drop the whole [..] block
106 while src[p] != (0 as u8) { if src[p] == (93 as u8) { p = p + 1; break } p = p + 1 }
107 }
108 while src[p] != (0 as u8) { if cl_is_alnum(src[p]) == 1 { break } p = p + 1 }
109 var k: i64 = 0
110 while src[p] != (0 as u8) { out[k] = src[p]; k = k + 1; p = p + 1 }
111 out[k] = 0 as u8
112 return k
113}
114
115// clean a subject: strip leading non-alpha, then drop leading stopwords
116func cl_clean_subject(src: *u8, out: *u8) -> i64 {
117 cl_copy_from_alpha(src, out)
118 let tok: *u8 = sys_mmap(256)
119 var go: i64 = 1
120 while go == 1 {
121 let tl: i64 = cl_first_token(out, tok)
122 if tl == 0 { go = 0 }
123 else { if cl_is_stopword(tok) == 1 { cl_drop_first_token(out, tl) } else { go = 0 } }
124 }
125 return cl_strlen(out)
126}
127
128// verb-led subject -> clause fragment, not an entity -> reject (rung R3b)
129func cl_is_verb(t: *u8) -> i64 {
130 if cl_streq_ci(t, "is" as *u8) == 1 { return 1 }
131 if cl_streq_ci(t, "are" as *u8) == 1 { return 1 }
132 if cl_streq_ci(t, "was" as *u8) == 1 { return 1 }
133 if cl_streq_ci(t, "were" as *u8) == 1 { return 1 }
134 if cl_streq_ci(t, "be" as *u8) == 1 { return 1 }
135 if cl_streq_ci(t, "been" as *u8) == 1 { return 1 }
136 if cl_streq_ci(t, "have" as *u8) == 1 { return 1 }
137 if cl_streq_ci(t, "has" as *u8) == 1 { return 1 }
138 if cl_streq_ci(t, "had" as *u8) == 1 { return 1 }
139 if cl_streq_ci(t, "provide" as *u8) == 1 { return 1 }
140 if cl_streq_ci(t, "provides" as *u8) == 1 { return 1 }
141 if cl_streq_ci(t, "provided" as *u8) == 1 { return 1 }
142 if cl_streq_ci(t, "remains" as *u8) == 1 { return 1 }
143 if cl_streq_ci(t, "remain" as *u8) == 1 { return 1 }
144 if cl_streq_ci(t, "found" as *u8) == 1 { return 1 }
145 if cl_streq_ci(t, "find" as *u8) == 1 { return 1 }
146 if cl_streq_ci(t, "use" as *u8) == 1 { return 1 }
147 if cl_streq_ci(t, "uses" as *u8) == 1 { return 1 }
148 if cl_streq_ci(t, "used" as *u8) == 1 { return 1 }
149 if cl_streq_ci(t, "make" as *u8) == 1 { return 1 }
150 if cl_streq_ci(t, "makes" as *u8) == 1 { return 1 }
151 if cl_streq_ci(t, "include" as *u8) == 1 { return 1 }
152 if cl_streq_ci(t, "includes" as *u8) == 1 { return 1 }
153 if cl_streq_ci(t, "allow" as *u8) == 1 { return 1 }
154 if cl_streq_ci(t, "allows" as *u8) == 1 { return 1 }
155 if cl_streq_ci(t, "enable" as *u8) == 1 { return 1 }
156 if cl_streq_ci(t, "enables" as *u8) == 1 { return 1 }
157 if cl_streq_ci(t, "require" as *u8) == 1 { return 1 }
158 if cl_streq_ci(t, "requires" as *u8) == 1 { return 1 }
159 if cl_streq_ci(t, "can" as *u8) == 1 { return 1 }
160 if cl_streq_ci(t, "will" as *u8) == 1 { return 1 }
161 if cl_streq_ci(t, "do" as *u8) == 1 { return 1 }
162 if cl_streq_ci(t, "does" as *u8) == 1 { return 1 }
163 return 0
164}
165
166// MAIN: subj_in,obj_in -> cleaned subj_out,obj_out; returns 1 GOOD | 0 REJECT.
167func nx_claim_clean(subj_in: *u8, obj_in: *u8, subj_out: *u8, obj_out: *u8) -> i64 {
168 cl_clean_subject(subj_in, subj_out)
169 cl_copy_from_alpha(obj_in, obj_out)
170 if cl_strlen(subj_out) < 2 { return 0 }
171 if cl_strlen(obj_out) < 2 { return 0 }
172 let tok: *u8 = sys_mmap(256)
173 cl_first_token(subj_out, tok)
174 if cl_is_pronoun(tok) == 1 { return 0 }
175 if cl_is_verb(tok) == 1 { return 0 }
176 if cl_word_count(subj_out) > cl_max_subj_words() { return 0 }
177 if cl_boiler(subj_out) == 1 { return 0 }
178 if cl_boiler(obj_out) == 1 { return 0 }
179 return 1
180}