code wiki / _hdl_build / nx_claim_extract.nx

nx_claim_extract.nx source

↩ module page · 133 lines · 5759 B

1// nx_claim_extract.nx -- MECHANIZED CLAIM EXTRACTION FROM RAW PROSE (closes the one axis where the 2// Claude deep-research still led: it used an LLM to pull claims from prose; this is the deterministic, 3// REPRODUCIBLE analog). A research claim, mechanically, is a fact-bearing sentence: one that carries a 4// MEASURED QUANTITY (a number adjacent to a unit -- "10 um gate", "952 transistors", "20 cm2/Vs", 5// "94% yield", "8 MHz"). The extractor segments prose into sentences, flags the fact-bearing ones, and 6// pulls the value+unit -- turning free prose into structured claim candidates that rs_scan + 7// rx_contested then corroborate/verify. Same input bytes -> same claims, every run (the LLM can't 8// promise that). 9// HONEST RESIDUAL (flagged, not hidden): this extracts MEASURABLE facts; nuanced/implicit/qualitative 10// claims ("X has largely supplanted Y") still need richer NLP -- the narrowed remaining gap. 11// LAWS: struct-free, integer-only. license_tier: ORIGINAL 12import "nx_syscalls.nx" 13 14func ce_is_digit(c: i64) -> i64 { if c >= 48 { if c <= 57 { return 1 } } return 0 } 15 16func ce_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 17 18// does NUL-terminated needle occur at buf[pos] (within [0,n))? (case-sensitive) 19func ce_match_at(buf: *u8, pos: i64, n: i64, needle: *u8) -> i64 { 20 var k: i64 = 0 21 while needle[k] != (0 as u8) { 22 if pos + k >= n { return 0 } 23 if (buf[pos+k] & 0xff) != (needle[k] & 0xff) { return 0 } 24 k = k + 1 25 } 26 return 1 27} 28 29// is a UNIT token present anywhere in buf[start,end)? returns 1/0. The unit vocabulary is the 30// measurement surface real research uses (calibrated to our corpora, extendable, data-not-code-ideal). 31func ce_has_unit(buf: *u8, start: i64, end: i64) -> i64 { 32 var i: i64 = start 33 while i < end { 34 if ce_match_at(buf, i, end, "um" as *u8) == 1 { return 1 } 35 if ce_match_at(buf, i, end, "nm" as *u8) == 1 { return 1 } 36 if ce_match_at(buf, i, end, "mm" as *u8) == 1 { return 1 } 37 if ce_match_at(buf, i, end, "%" as *u8) == 1 { return 1 } 38 if ce_match_at(buf, i, end, "MHz" as *u8) == 1 { return 1 } 39 if ce_match_at(buf, i, end, "GHz" as *u8) == 1 { return 1 } 40 if ce_match_at(buf, i, end, "cm" as *u8) == 1 { return 1 } 41 if ce_match_at(buf, i, end, "transistor" as *u8) == 1 { return 1 } 42 if ce_match_at(buf, i, end, "micron" as *u8) == 1 { return 1 } 43 if ce_match_at(buf, i, end, "fL" as *u8) == 1 { return 1 } 44 if ce_match_at(buf, i, end, "yield" as *u8) == 1 { return 1 } 45 if ce_match_at(buf, i, end, "node" as *u8) == 1 { return 1 } 46 i = i + 1 47 } 48 return 0 49} 50 51// is there a digit in buf[start,end)? 52func ce_has_digit(buf: *u8, start: i64, end: i64) -> i64 { 53 var i: i64 = start 54 while i < end { if ce_is_digit(buf[i] as i64) == 1 { return 1 } i = i + 1 } 55 return 0 56} 57 58// a sentence is FACT-BEARING iff it carries a number AND a unit (a measured quantity = a claim) 59func ce_is_fact_sentence(buf: *u8, start: i64, end: i64) -> i64 { 60 if ce_has_digit(buf, start, end) == 0 { return 0 } 61 if ce_has_unit(buf, start, end) == 0 { return 0 } 62 return 1 63} 64 65// is c an ASCII letter? 66func ce_is_alpha(c: i64) -> i64 { 67 if c >= 97 { if c <= 122 { return 1 } } 68 if c >= 65 { if c <= 90 { return 1 } } 69 return 0 70} 71 72// first MEASUREMENT value in buf[start,end) (-1 if none). A measurement number is NOT glued to a 73// preceding letter -- that excludes name digits like the "2" in "Z2", keeping "952". 74func ce_first_value(buf: *u8, start: i64, end: i64) -> i64 { 75 var i: i64 = start 76 while i < end { 77 if ce_is_digit(buf[i] as i64) == 1 { 78 var preceded_by_letter: i64 = 0 79 if i > start { if ce_is_alpha(buf[i-1] as i64) == 1 { preceded_by_letter = 1 } } 80 if preceded_by_letter == 0 { 81 var v: i64 = 0 82 while i < end { if ce_is_digit(buf[i] as i64) == 1 { v = v*10 + (buf[i] as i64 - 48); i = i + 1 } else { break } } 83 return v 84 } 85 // skip this glued-to-letter digit run (advance past it), then keep scanning 86 while i < end { if ce_is_digit(buf[i] as i64) == 1 { i = i + 1 } else { break } } 87 } else { 88 i = i + 1 89 } 90 } 91 return 0 - 1 92} 93 94// scan prose into sentences (split on '.' or newline) and tally claims. out[0]=sentences 95// out[1]=fact_sentences(=claims_extracted) out[2]=first_claim_value. returns claims_extracted. 96func ce_extract(buf: *u8, n: i64, out: *i64) -> i64 { 97 var sentences: i64 = 0 98 var facts: i64 = 0 99 var first_val: i64 = 0 - 1 100 var start: i64 = 0 101 var i: i64 = 0 102 while i <= n { 103 var is_end: i64 = 0 104 if i == n { is_end = 1 } 105 if i < n { let c: i64 = buf[i] as i64; if c == 46 { is_end = 1 } if c == 10 { is_end = 1 } } // '.' or '\n' 106 if is_end == 1 { 107 if i > start { 108 sentences = sentences + 1 109 if ce_is_fact_sentence(buf, start, i) == 1 { 110 facts = facts + 1 111 if first_val < 0 { first_val = ce_first_value(buf, start, i) } 112 } 113 } 114 start = i + 1 115 } 116 i = i + 1 117 } 118 out[0] = sentences 119 out[1] = facts 120 out[2] = first_val 121 return facts 122} 123 124// read a file into buf; returns length (0 on fail) 125func ce_read_file(path: *u8, buf: *u8, cap: i64) -> i64 { 126 let fd: i64 = sys_openat_rd(path) 127 if fd < 0 { return 0 } 128 var total: i64 = 0 129 var r: i64 = sys_read(fd, buf as *u8, cap) 130 while r > 0 { total = total + r; if total >= cap { r = 0 } else { r = sys_read(fd, (buf as i64 + total) as *u8, cap - total) } } 131 sys_close(fd) 132 return total 133}