nx_xbrl_lib.nx source
↩ module page · 113 lines · 4811 B
1// nx_xbrl_lib.nx -- F987: XBRL financial-fact parse + accounting-identity validation.
2//
3// SEC filings are tagged in XBRL: <us-gaap:Assets contextRef=... unitRef=usd decimals=0>1000000</us-gaap:Assets>.
4// The sovereign property is that the parsed facts are CROSS-CHECKED against the accounting identity
5// (Assets == Liabilities + StockholdersEquity), so a mis-tagged or internally-inconsistent filing is
6// FLAGGED rather than fed blindly into a valuation. The same fail-closed discipline as the derived
7// statements: a balance sheet -- even one someone else prepared and tagged -- must balance, or it is
8// rejected. These parsed integers are exactly what feeds the distress (Altman Z) and stewardship engines
9// with GROUNDED numbers instead of hand-entered ones.
10//
11// ATTRIBUTE-AWARE + NAMESPACE-AWARE by word boundary: matching <us-gaap:Assets checks the next byte is
12// '>' or whitespace, so it never mis-matches <us-gaap:AssetsCurrent>. That boundary check is the whole
13// correctness of a tag parser and it is done explicitly.
14//
15// FAIL-CLOSED: a missing required fact is a rejection (a sentinel), not a zero -- a balance sheet with
16// no Assets tag is not a balance sheet with zero assets.
17//
18// SCOPE (declared): parses fact text + validates the balance identity. Full XBRL (contexts, dimensions,
19// calculation linkbases, negative decimals scaling) is a declared follow-on. Values are whole units here.
20// DRY: composes nx_iso20022_lib (iso_find/iso_len). license_tier: ORIGINAL LIB.
21
22import "nx_iso20022_lib.nx"
23
24const XB_NO_FACT: i64 = 0 - 999999999 // sentinel: a required fact is absent (fail-closed)
25
26// parse a signed integer from text (whole units).
27func xb_atoi(s: *u8) -> i64 {
28 var v: i64 = 0
29 var i: i64 = 0
30 var neg: i64 = 0
31 if s[0] == (45 as u8) { neg = 1; i = 1 }
32 while s[i] != (0 as u8) {
33 if s[i] >= 48 as u8 { if s[i] <= 57 as u8 { v = v * 10 + (s[i] - 48) } }
34 i = i + 1
35 }
36 if neg == 1 { return 0 - v }
37 return v
38}
39
40// extract the text of <concept ...>text</concept> (attribute- and namespace-boundary aware).
41// returns length, or -1 if the concept is absent. out must be sized for the value.
42func xbrl_fact(xml: *u8, xl: i64, concept: *u8, out: *u8) -> i64 {
43 // opentag = "<concept" (no '>' -- attributes may follow)
44 let ot: *u8 = sys_mmap(256)
45 ot[0] = 60 as u8
46 var ol: i64 = 1
47 var ci: i64 = 0
48 while concept[ci] != (0 as u8) { ot[ol] = concept[ci]; ol = ol + 1; ci = ci + 1 }
49 ot[ol] = 0 as u8
50 // closetag = "</concept>"
51 let ct: *u8 = sys_mmap(256)
52 ct[0] = 60 as u8
53 ct[1] = 47 as u8
54 var cl: i64 = 2
55 ci = 0
56 while concept[ci] != (0 as u8) { ct[cl] = concept[ci]; cl = cl + 1; ci = ci + 1 }
57 ct[cl] = 62 as u8
58 cl = cl + 1
59 ct[cl] = 0 as u8
60 let gtneedle: *u8 = ">" as *u8
61 var i: i64 = 0
62 var go: i64 = 1
63 while go == 1 {
64 let p: i64 = iso_find(xml, xl, ot, ol, i)
65 if p < 0 { return 0 - 1 }
66 let after: i64 = p + ol
67 var boundary: i64 = 0
68 if after < xl {
69 if xml[after] == (62 as u8) { boundary = 1 } // '>'
70 if xml[after] == (32 as u8) { boundary = 1 } // ' '
71 if xml[after] == (9 as u8) { boundary = 1 } // tab
72 }
73 if boundary == 1 {
74 let gt: i64 = iso_find(xml, xl, gtneedle, 1, after)
75 if gt < 0 { return 0 - 1 }
76 let cs: i64 = gt + 1
77 let ce: i64 = iso_find(xml, xl, ct, cl, cs)
78 if ce < 0 { return 0 - 1 }
79 var t: i64 = 0
80 var q: i64 = cs
81 while q < ce { out[t] = xml[q]; t = t + 1; q = q + 1 }
82 out[t] = 0 as u8
83 return t
84 }
85 i = p + 1
86 }
87 return 0 - 1
88}
89
90// parse a concept's value as an integer; XB_NO_FACT if absent (fail-closed).
91func xbrl_fact_int(xml: *u8, xl: i64, concept: *u8) -> i64 {
92 let buf: *u8 = sys_mmap(64)
93 if xbrl_fact(xml, xl, concept, buf) < 0 { return XB_NO_FACT }
94 return xb_atoi(buf)
95}
96
97// ★VALIDATE the balance-sheet identity from the tagged facts.
98// writes assets/liabilities/equity/variance to out[0..3].
99// returns 0 if Assets == Liabilities + Equity; XB_NO_FACT if any of the three is missing (fail-closed);
100// otherwise the non-zero variance (the filing does not balance -- flag it).
101func xbrl_check_balance(xml: *u8, xl: i64, out: *i64) -> i64 {
102 let a: i64 = xbrl_fact_int(xml, xl, "us-gaap:Assets" as *u8)
103 let l: i64 = xbrl_fact_int(xml, xl, "us-gaap:Liabilities" as *u8)
104 let e: i64 = xbrl_fact_int(xml, xl, "us-gaap:StockholdersEquity" as *u8)
105 if a == XB_NO_FACT { return XB_NO_FACT }
106 if l == XB_NO_FACT { return XB_NO_FACT }
107 if e == XB_NO_FACT { return XB_NO_FACT }
108 out[0] = a
109 out[1] = l
110 out[2] = e
111 out[3] = a - (l + e)
112 return out[3]
113}