1- /* compact_out.c — TOON emission helpers. See compact_out.h for the contract. */
1+ /* compact_out.c — tree-format emission helpers. See compact_out.h for the contract. */
22#include "mcp/compact_out.h"
33
44#include <ctype.h>
@@ -72,7 +72,7 @@ void cbm_sb_free(cbm_sb_t *sb) {
7272 cbm_sb_init (sb );
7373}
7474
75- /* ── TOON quoting ───────────────────────────────────────────────── */
75+ /* ── Tree quoting ─ ───────────────────────────────────────────────── */
7676
7777/* True when `s` parses as an integer or real literal (sign, digits, one dot). */
7878static bool looks_numeric (const char * s ) {
@@ -99,17 +99,17 @@ static bool looks_numeric(const char *s) {
9999
100100static bool needs_quotes (const char * s ) {
101101 if (!s || !* s ) {
102- return true; /* empty cell must be visible as "" */
103- }
104- if (isspace ((unsigned char )s [0 ]) || isspace ((unsigned char )s [strlen (s ) - 1 ])) {
105- return true;
102+ return false; /* empty cells emit as the "-" placeholder, not quotes */
106103 }
107104 for (const char * p = s ; * p ; p ++ ) {
108- if (* p == ',' || * p == '"' || * p == '\n' || * p == '\r' ) {
105+ /* Space-delimited rows: any internal whitespace or quote forces
106+ * quoting so column positions stay parseable. */
107+ if (isspace ((unsigned char )* p ) || * p == '"' || * p == '\r' ) {
109108 return true;
110109 }
111110 }
112- if (strcmp (s , "true" ) == 0 || strcmp (s , "false" ) == 0 || strcmp (s , "null" ) == 0 ) {
111+ if (strcmp (s , "true" ) == 0 || strcmp (s , "false" ) == 0 || strcmp (s , "null" ) == 0 ||
112+ strcmp (s , "-" ) == 0 ) {
113113 return true;
114114 }
115115 return looks_numeric (s );
@@ -140,6 +140,10 @@ static void append_quoted(cbm_sb_t *sb, const char *s) {
140140}
141141
142142static void append_value (cbm_sb_t * sb , const char * s ) {
143+ if (!s || !* s ) {
144+ cbm_sb_append_n (sb , "-" , 1 ); /* stable column positions for empties */
145+ return ;
146+ }
143147 if (needs_quotes (s )) {
144148 append_quoted (sb , s );
145149 } else {
@@ -149,14 +153,14 @@ static void append_value(cbm_sb_t *sb, const char *s) {
149153
150154/* ── Scalars ────────────────────────────────────────────────────── */
151155
152- void cbm_toon_scalar_str (cbm_sb_t * sb , const char * key , const char * val ) {
156+ void cbm_tree_scalar_str (cbm_sb_t * sb , const char * key , const char * val ) {
153157 cbm_sb_append (sb , key );
154158 cbm_sb_append_n (sb , ": " , 2 );
155159 append_value (sb , val );
156160 cbm_sb_append_n (sb , "\n" , 1 );
157161}
158162
159- void cbm_toon_scalar_int (cbm_sb_t * sb , const char * key , long long v ) {
163+ void cbm_tree_scalar_int (cbm_sb_t * sb , const char * key , long long v ) {
160164 char num [32 ];
161165 snprintf (num , sizeof (num ), "%lld" , v );
162166 cbm_sb_append (sb , key );
@@ -165,7 +169,7 @@ void cbm_toon_scalar_int(cbm_sb_t *sb, const char *key, long long v) {
165169 cbm_sb_append_n (sb , "\n" , 1 );
166170}
167171
168- void cbm_toon_scalar_bool (cbm_sb_t * sb , const char * key , bool v ) {
172+ void cbm_tree_scalar_bool (cbm_sb_t * sb , const char * key , bool v ) {
169173 cbm_sb_append (sb , key );
170174 cbm_sb_append_n (sb , ": " , 2 );
171175 cbm_sb_append (sb , v ? "true" : "false" );
@@ -174,57 +178,57 @@ void cbm_toon_scalar_bool(cbm_sb_t *sb, const char *key, bool v) {
174178
175179/* ── Tables ─────────────────────────────────────────────────────── */
176180
177- void cbm_toon_table_header (cbm_sb_t * sb , const char * key , int n , const char * const * cols ,
181+ /* Tree-syntax table header: `key: N (cols: a b c)` — count first (agents
182+ * read scale before rows), column names once, rows indented beneath. */
183+ void cbm_tree_table_header (cbm_sb_t * sb , const char * key , int n , const char * const * cols ,
178184 int ncols ) {
179185 char num [32 ];
180- snprintf (num , sizeof (num ), "[%d]{ " , n );
186+ snprintf (num , sizeof (num ), ": %d (cols: " , n );
181187 cbm_sb_append (sb , key );
182188 cbm_sb_append (sb , num );
183189 for (int i = 0 ; i < ncols ; i ++ ) {
184- if (i > 0 ) {
185- cbm_sb_append_n (sb , "," , 1 );
186- }
190+ cbm_sb_append_n (sb , " " , 1 );
187191 cbm_sb_append (sb , cols [i ]);
188192 }
189- cbm_sb_append_n (sb , "}: \n" , 3 );
193+ cbm_sb_append_n (sb , ") \n" , 2 );
190194}
191195
192- void cbm_toon_row_begin (cbm_sb_t * sb ) {
196+ void cbm_tree_row_begin (cbm_sb_t * sb ) {
193197 cbm_sb_append_n (sb , " " , 2 );
194198}
195199
196- void cbm_toon_cell_str (cbm_sb_t * sb , const char * val , bool first ) {
200+ void cbm_tree_cell_str (cbm_sb_t * sb , const char * val , bool first ) {
197201 if (!first ) {
198- cbm_sb_append_n (sb , ", " , 1 );
202+ cbm_sb_append_n (sb , " " , 1 );
199203 }
200204 append_value (sb , val ? val : "" );
201205}
202206
203- void cbm_toon_cell_int (cbm_sb_t * sb , long long v , bool first ) {
207+ void cbm_tree_cell_int (cbm_sb_t * sb , long long v , bool first ) {
204208 char num [32 ];
205209 snprintf (num , sizeof (num ), "%lld" , v );
206210 if (!first ) {
207- cbm_sb_append_n (sb , ", " , 1 );
211+ cbm_sb_append_n (sb , " " , 1 );
208212 }
209213 cbm_sb_append (sb , num );
210214}
211215
212- void cbm_toon_cell_real (cbm_sb_t * sb , double v , bool first ) {
216+ void cbm_tree_cell_real (cbm_sb_t * sb , double v , bool first ) {
213217 char num [48 ];
214218 snprintf (num , sizeof (num ), "%.4g" , v );
215219 if (!first ) {
216- cbm_sb_append_n (sb , ", " , 1 );
220+ cbm_sb_append_n (sb , " " , 1 );
217221 }
218222 cbm_sb_append (sb , num );
219223}
220224
221- void cbm_toon_cell_bool (cbm_sb_t * sb , bool v , bool first ) {
225+ void cbm_tree_cell_bool (cbm_sb_t * sb , bool v , bool first ) {
222226 if (!first ) {
223- cbm_sb_append_n (sb , ", " , 1 );
227+ cbm_sb_append_n (sb , " " , 1 );
224228 }
225229 cbm_sb_append (sb , v ? "true" : "false" );
226230}
227231
228- void cbm_toon_row_end (cbm_sb_t * sb ) {
232+ void cbm_tree_row_end (cbm_sb_t * sb ) {
229233 cbm_sb_append_n (sb , "\n" , 1 );
230234}
0 commit comments