code wiki / (root) / nx_grammar.nx

nx_grammar.nx source

↩ module page · 127 lines · 4547 B

1// nx_grammar.nx -- WRITING arc, rung W-R2g: MECHANICAL grammar checks. 2// Content-agnostic, deterministic, dictionary-FREE. Honest scope: these are the 3// mechanical checks an editor catches by pattern, NOT full ML/Grammarly-class 4// grammar -- dictionary spell-check + suggestion ranking is a later sub-arc. This 5// fills the lone honest gap in the W-EXC-1 census with a real, exact organ. 6// 7// Checks (out[], filled by gr_check): 8// out[0] = doubled words -- the same word twice in a row ("the the") 9// out[1] = a/an misuse -- "a" before a vowel, or "an" before a consonant 10// (letter heuristic; a/e/i/o/u are vowels, not y/h) 11// out[2] = cap-after-period -- a lowercase word right after . ! or ? 12// returns the total error count. 13// 14// Pure integer, NO syscalls. Reuses nx_writecraft for char classification (DRY). 15// 16// license_tier: ORIGINAL 17// module: nishi-core.write.grammar 18// depends: nishi-core.write.craft 19// capability: WRITE_MECHANICAL_GRAMMAR 20import "nx_writecraft.nx" 21 22// 1 if c (any case) is an article-vowel a/e/i/o/u (NOT y, NOT h) 23func gr_is_art_vowel(c: i64) -> i64 { 24 let l: i64 = wc_lower(c) 25 if l == 97 { return 1 } 26 if l == 101 { return 1 } 27 if l == 105 { return 1 } 28 if l == 111 { return 1 } 29 if l == 117 { return 1 } 30 return 0 31} 32 33// 1 if t[a..b) equals null-terminated lit, case-insensitively 34func gr_eq_lit(t: *u8, a: i64, b: i64, lit: *u8) -> i64 { 35 var i: i64 = a 36 var j: i64 = 0 37 while i < b { 38 if lit[j] == (0 as u8) { return 0 } 39 if wc_lower(t[i] as i64) != wc_lower(lit[j] as i64) { return 0 } 40 i = i + 1 41 j = j + 1 42 } 43 if lit[j] != (0 as u8) { return 0 } 44 return 1 45} 46 47// 1 if the two word ranges are equal case-insensitively 48func gr_words_eq(t: *u8, a1: i64, b1: i64, a2: i64, b2: i64) -> i64 { 49 if b1 - a1 != b2 - a2 { return 0 } 50 var i: i64 = 0 51 while i < b1 - a1 { 52 if wc_lower(t[a1 + i] as i64) != wc_lower(t[a2 + i] as i64) { return 0 } 53 i = i + 1 54 } 55 return 1 56} 57 58func gr_check(t: *u8, n: i64, out: *i64) -> i64 { 59 var doubled: i64 = 0 60 var aan: i64 = 0 61 var caperr: i64 = 0 62 var pstart: i64 = 0 - 1 // previous word range 63 var pend: i64 = 0 - 1 64 var prev_a: i64 = 0 // previous word was "a" 65 var prev_an: i64 = 0 // previous word was "an" 66 var prev_term: i64 = 0 // a sentence terminator since the last word 67 var wstart: i64 = 0 - 1 68 var i: i64 = 0 69 while i < n { 70 let c: i64 = t[i] as i64 71 var inword: i64 = 0 72 if wc_is_alpha(c) == 1 { 73 inword = 1 74 } else { 75 if c == 39 { if wstart >= 0 { inword = 1 } } 76 } 77 if inword == 1 { 78 if wstart < 0 { 79 wstart = i 80 if prev_term == 1 { // first word after . ! ? 81 if c >= 97 { if c <= 122 { caperr = caperr + 1 } } 82 } 83 prev_term = 0 84 } 85 } else { 86 if wstart >= 0 { 87 // doubled-word 88 if pstart >= 0 { 89 if gr_words_eq(t, pstart, pend, wstart, i) == 1 { doubled = doubled + 1 } 90 } 91 // a/an vs this word's first letter 92 if prev_a == 1 { 93 if gr_is_art_vowel(t[wstart] as i64) == 1 { aan = aan + 1 } 94 } 95 if prev_an == 1 { 96 if gr_is_art_vowel(t[wstart] as i64) == 0 { aan = aan + 1 } 97 } 98 prev_a = 0 99 prev_an = 0 100 if gr_eq_lit(t, wstart, i, "a\x00" as *u8) == 1 { prev_a = 1 } 101 if gr_eq_lit(t, wstart, i, "an\x00" as *u8) == 1 { prev_an = 1 } 102 pstart = wstart 103 pend = i 104 wstart = 0 - 1 105 } 106 if c == 46 { prev_term = 1 } 107 if c == 33 { prev_term = 1 } 108 if c == 63 { prev_term = 1 } 109 } 110 i = i + 1 111 } 112 if wstart >= 0 { // trailing word 113 if pstart >= 0 { 114 if gr_words_eq(t, pstart, pend, wstart, n) == 1 { doubled = doubled + 1 } 115 } 116 if prev_a == 1 { 117 if gr_is_art_vowel(t[wstart] as i64) == 1 { aan = aan + 1 } 118 } 119 if prev_an == 1 { 120 if gr_is_art_vowel(t[wstart] as i64) == 0 { aan = aan + 1 } 121 } 122 } 123 out[0] = doubled 124 out[1] = aan 125 out[2] = caperr 126 return doubled + aan + caperr 127}