nx_rfc_ingest.nx source
↩ module page · 206 lines · 7027 B
1// nx_rfc_ingest.nx -- ingest IETF RFC plaintext.
2//
3// RFCs (rfc-editor.org) are PD-equivalent under the IETF Trust Legal
4// Provisions and are an enormous source of clean, well-specified
5// algorithms. This parser walks plaintext .txt files and extracts
6// numbered section headers, which in practice name the algorithms and
7// procedures defined within the RFC.
8//
9// Recognized pattern (line-anchored):
10// ^[ ]{0,3}<digits>(\.<digits>)*\.?[ ]+<Title>...
11// Examples:
12// "1. Executive Summary"
13// "3.1. Algorithm Description"
14// "4.1.1. Step Definition"
15//
16// The decl `name` is the title text (whitespace trimmed).
17//
18// genealogy_id: ietf_trust_legal_provisions_2025
19// lineage_id: plaintext_section_header_parser
20
21// nx_safety_envelope:
22// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
23// sil_target: SIL1
24// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
25// verdict: NOT_YET_EVALUATED
26
27import "nx_syscalls.nx"
28import "nx_tier.nx"
29
30// ===== sealed kinds =================================================
31
32const NX_RFC_KIND_SECTION: nx_int = 1
33const NX_RFC_KIND_DEFINITION: nx_int = 2
34
35const NX_RFC_MAX_NAME_LEN: nx_int = 256
36const NX_RFC_MAX_DECLS: nx_int = 65536
37
38// ===== entry structures =============================================
39
40struct RfcDecl {
41 kind: nx_int,
42 name: *u8,
43}
44
45const NX_RFC_DECL_BYTES: nx_int = 16
46
47struct RfcDb {
48 decls: *RfcDecl,
49 n_decls: nx_int,
50 capacity: nx_int,
51}
52
53func nx_rfc_db_alloc() -> *RfcDb {
54 let raw: *u8 = sys_mmap(24)
55 let db: *RfcDb = raw as *RfcDb
56 db.decls = (sys_mmap((NX_RFC_MAX_DECLS as nx_size) * (NX_RFC_DECL_BYTES as nx_size))) as *RfcDecl
57 db.n_decls = 0
58 db.capacity = NX_RFC_MAX_DECLS
59 return db
60}
61
62func nx_rfc_decl_at(db: *RfcDb, i: nx_int) -> *RfcDecl {
63 return (((db.decls as nx_size) + (i as nx_size) * (NX_RFC_DECL_BYTES as nx_size)) as *RfcDecl)
64}
65
66// ===== helpers ======================================================
67
68// Is a byte a decimal digit?
69func nx_rfc_is_digit(c: nx_int) -> nx_int {
70 if c < 0x30 { return 0 }
71 if c > 0x39 { return 0 }
72 return 1
73}
74
75// Sanitize a title character for use as an identifier-like name:
76// alphanumeric stays; space + punctuation -> '_'; trim trailing _.
77func nx_rfc_sanitize_char(c: nx_int) -> nx_int {
78 if c >= 0x30 { if c <= 0x39 { return c } } // 0-9
79 if c >= 0x41 { if c <= 0x5A { return c } } // A-Z
80 if c >= 0x61 { if c <= 0x7A { return c } } // a-z
81 return 0x5F // '_'
82}
83
84// Skip leading whitespace within [pos, eol). Returns the first non-ws
85// index. Whitespace = space (0x20) or tab (0x09).
86func nx_rfc_skip_ws(buf: *u8, pos: nx_int, eol: nx_int) -> nx_int {
87 var p: nx_int = pos
88 var done: nx_int = 0
89 while done == 0 {
90 if p >= eol { done = 1 }
91 if done == 0 {
92 let c: nx_int = buf[p] as nx_int
93 if c == 0x20 { p = p + 1 }
94 if c == 0x09 { p = p + 1 }
95 if c != 0x20 {
96 if c != 0x09 { done = 1 }
97 }
98 }
99 }
100 return p
101}
102
103// Check whether the line [pos, eol) matches the section-header pattern:
104// optional 0-3 spaces, digits + dots (e.g., "3.1.2"), optional final dot,
105// one or more spaces, then title text.
106// Returns the index where the title starts (after the dot-leader and spaces),
107// or -1 if no match.
108func nx_rfc_section_title_start(buf: *u8, pos: nx_int, eol: nx_int) -> nx_int {
109 let after_ws: nx_int = nx_rfc_skip_ws(buf, pos, eol)
110 if after_ws >= eol { return -1 }
111 if nx_rfc_is_digit(buf[after_ws] as nx_int) == 0 { return -1 }
112 // Must have at most 3 leading spaces (this excludes deeply-indented
113 // pseudocode that happens to start with a digit).
114 if (after_ws - pos) > 3 { return -1 }
115 var p: nx_int = after_ws
116 var saw_digit: nx_int = 0
117 var saw_dot: nx_int = 0
118 var done: nx_int = 0
119 while done == 0 {
120 if p >= eol { done = 1 }
121 if done == 0 {
122 let c: nx_int = buf[p] as nx_int
123 if nx_rfc_is_digit(c) == 1 { saw_digit = 1; p = p + 1 }
124 if c == 0x2E { saw_dot = 1; p = p + 1 } // '.'
125 if c != 0x2E {
126 if nx_rfc_is_digit(c) == 0 { done = 1 }
127 }
128 }
129 }
130 if saw_digit == 0 { return -1 }
131 if saw_dot == 0 { return -1 } // require at least one '.' (filters bare line numbers)
132 // Need at least one space after the dot-leader.
133 if p >= eol { return -1 }
134 let c2: nx_int = buf[p] as nx_int
135 if c2 != 0x20 { if c2 != 0x09 { return -1 } }
136 return nx_rfc_skip_ws(buf, p, eol)
137}
138
139// Copy title text into out (sanitized, trimmed of trailing _). Caps at
140// NX_RFC_MAX_NAME_LEN. Returns the written length.
141func nx_rfc_emit_title(buf: *u8, lo: nx_int, eol: nx_int, out: *u8) -> nx_int {
142 var i: nx_int = 0
143 var p: nx_int = lo
144 while p < eol {
145 if i < (NX_RFC_MAX_NAME_LEN - 1) {
146 let c: nx_int = buf[p] as nx_int
147 // Page-number filler: trailing spaces + final number.
148 // Stop at runs of more than 2 spaces (typical TOC dot-leader).
149 if c == 0x20 {
150 if p + 1 < eol {
151 if buf[p + 1] == 0x20 {
152 // Two-or-more spaces -> end of meaningful title.
153 p = eol + 1
154 }
155 }
156 }
157 if p <= eol {
158 out[i] = nx_rfc_sanitize_char(c) as u8
159 i = i + 1
160 }
161 }
162 p = p + 1
163 }
164 // Trim trailing underscores.
165 while i > 0 {
166 if out[i - 1] == 0x5F { i = i - 1 }
167 if out[i - 1] != 0x5F { out[i] = 0; return i }
168 }
169 out[i] = 0
170 return i
171}
172
173// ===== ingest driver ================================================
174
175func nx_rfc_ingest_corpus(buf: *u8, len: nx_int) -> *RfcDb {
176 let db: *RfcDb = nx_rfc_db_alloc()
177 var pos: nx_int = 0
178 while pos < len {
179 // Find end of line.
180 var eol: nx_int = pos
181 var done: nx_int = 0
182 while done == 0 {
183 if eol >= len { done = 1 }
184 if done == 0 {
185 if buf[eol] == 10 { done = 1 }
186 if done == 0 { eol = eol + 1 }
187 }
188 }
189 // Try the section-header pattern.
190 let tstart: nx_int = nx_rfc_section_title_start(buf, pos, eol)
191 if tstart >= 0 {
192 if db.n_decls < db.capacity {
193 let name_buf: *u8 = sys_mmap(NX_RFC_MAX_NAME_LEN as nx_size)
194 let n_emitted: nx_int = nx_rfc_emit_title(buf, tstart, eol, name_buf)
195 if n_emitted > 0 {
196 let d: *RfcDecl = nx_rfc_decl_at(db, db.n_decls)
197 d.kind = NX_RFC_KIND_SECTION
198 d.name = name_buf
199 db.n_decls = db.n_decls + 1
200 }
201 }
202 }
203 pos = eol + 1
204 }
205 return db
206}