popline-py 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ include popline.h
@@ -0,0 +1,73 @@
1
+ Metadata-Version: 2.4
2
+ Name: popline-py
3
+ Version: 0.4.0
4
+ Summary: PopLine — Line-oriented serialization format (C extension)
5
+ Home-page: https://github.com/one18mb/popline-py
6
+ Author: one18mb
7
+ Author-email: 1915182174@qq.com
8
+ License: MIT
9
+ Project-URL: Source, https://github.com/one18mb/popline-py
10
+ Project-URL: Spec, https://github.com/one18mb/popline
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: C
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Software Development :: Libraries
17
+ Classifier: Topic :: Text Processing :: Markup
18
+ Requires-Python: >=3.6
19
+ Description-Content-Type: text/markdown
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: license
27
+ Dynamic: project-url
28
+ Dynamic: requires-python
29
+ Dynamic: summary
30
+
31
+ # PopLine Python
32
+
33
+ PopLine 序列化格式的 Python C 扩展实现。零外部依赖。
34
+
35
+ ## 安装
36
+
37
+ ```bash
38
+ pip install popline-py
39
+ ```
40
+
41
+ ## 使用
42
+
43
+ ```python
44
+ import pln
45
+
46
+ # PopLine → Python 对象
47
+ obj = pln.loads('{\nkey: "value"\n')
48
+ # → {"key": "value"}
49
+
50
+ # Python 对象 → PopLine
51
+ text = pln.dumps({"key": "value"})
52
+ # → '{\nkey: "value"\n'
53
+ ```
54
+
55
+ ## 性能
56
+
57
+ 测试数据:`package.json`(17011 B)→ `package.pln`(13074 B,**76.9%**),5000 次迭代
58
+
59
+ | 操作 | Python json | pln | 比 |
60
+ |------|------------|---------|------|
61
+ | 解析 | 689 ms (137 µs/op) | 626 ms (125 µs/op) | **0.91x** |
62
+ | 序列化 | 935 ms (187 µs/op) | 213 ms (42 µs/op) | **0.23x** |
63
+
64
+ ## 测试
65
+
66
+ ```bash
67
+ python test.py
68
+ ```
69
+
70
+ ## 致谢
71
+ 本项目的开发得到了以下 AI 工具的大力协助:
72
+ - [Claude Code](https://claude.ai)(Anthropic)
73
+ - [DeepSeek](https://deepseek.com)(深度求索)
@@ -0,0 +1,43 @@
1
+ # PopLine Python
2
+
3
+ PopLine 序列化格式的 Python C 扩展实现。零外部依赖。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ pip install popline-py
9
+ ```
10
+
11
+ ## 使用
12
+
13
+ ```python
14
+ import pln
15
+
16
+ # PopLine → Python 对象
17
+ obj = pln.loads('{\nkey: "value"\n')
18
+ # → {"key": "value"}
19
+
20
+ # Python 对象 → PopLine
21
+ text = pln.dumps({"key": "value"})
22
+ # → '{\nkey: "value"\n'
23
+ ```
24
+
25
+ ## 性能
26
+
27
+ 测试数据:`package.json`(17011 B)→ `package.pln`(13074 B,**76.9%**),5000 次迭代
28
+
29
+ | 操作 | Python json | pln | 比 |
30
+ |------|------------|---------|------|
31
+ | 解析 | 689 ms (137 µs/op) | 626 ms (125 µs/op) | **0.91x** |
32
+ | 序列化 | 935 ms (187 µs/op) | 213 ms (42 µs/op) | **0.23x** |
33
+
34
+ ## 测试
35
+
36
+ ```bash
37
+ python test.py
38
+ ```
39
+
40
+ ## 致谢
41
+ 本项目的开发得到了以下 AI 工具的大力协助:
42
+ - [Claude Code](https://claude.ai)(Anthropic)
43
+ - [DeepSeek](https://deepseek.com)(深度求索)
@@ -0,0 +1,392 @@
1
+ /* popline.c — PopLine v2 C reference implementation (optimized) */
2
+
3
+ #include "popline.h"
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+ #include <stdio.h>
7
+ #include <math.h>
8
+ #include <errno.h>
9
+
10
+ /* ─── 内存辅助 ─────────────────────────────────────────── */
11
+
12
+ static void *pln_realloc(void *p, size_t sz) {
13
+ void *r = realloc(p, sz);
14
+ if (!r && sz) { free(p); fprintf(stderr, "OOM\n"); abort(); }
15
+ return r;
16
+ }
17
+
18
+ static char *pln_strdup(const char *s) {
19
+ if (!s) return NULL;
20
+ size_t n = strlen(s) + 1;
21
+ char *d = (char *)malloc(n);
22
+ if (!d) { fprintf(stderr, "OOM\n"); abort(); }
23
+ memcpy(d, s, n);
24
+ return d;
25
+ }
26
+
27
+ static char *pln_strdupl(const char *s, int len) {
28
+ if (!s) return NULL;
29
+ char *d = (char *)malloc(len + 1);
30
+ if (!d) { fprintf(stderr, "OOM\n"); abort(); }
31
+ memcpy(d, s, len);
32
+ d[len] = '\0';
33
+ return d;
34
+ }
35
+
36
+ /* ─── 字符串转义 ────────────────────────────────────────── */
37
+
38
+ /* 就地转义:将 src[0..len-1] 写入 dst,处理后返回写入长度。
39
+ 如果 dst 为 NULL,只计算需要的长度。*/
40
+ int pln_unescape_to(const char *s, int len, char *dst) {
41
+ int j = 0;
42
+ for (int i = 0; i < len; ) {
43
+ if (s[i] == '"' && i + 1 < len && s[i + 1] == '"') {
44
+ if (dst) dst[j] = '"';
45
+ j++; i += 2;
46
+ } else {
47
+ if (dst) dst[j] = s[i];
48
+ j++; i++;
49
+ }
50
+ }
51
+ return j;
52
+ }
53
+
54
+ char *pln_unescape(const char *s, int len) {
55
+ int out_len = pln_unescape_to(s, len, NULL);
56
+ char *out = (char *)malloc(out_len + 1);
57
+ pln_unescape_to(s, len, out);
58
+ out[out_len] = '\0';
59
+ return out;
60
+ }
61
+
62
+
63
+
64
+ /* ─── 生成器 ─────────────────────────────────────────────── */
65
+
66
+ #define PLN_GEN_INIT_CAP 256
67
+
68
+ void pln_gen_init(pln_gen_t *g) {
69
+ memset(g, 0, sizeof(*g));
70
+ g->cap = PLN_GEN_INIT_CAP;
71
+ g->buf = (char *)malloc(g->cap);
72
+ g->stack_cap = 64;
73
+ g->stack = (char *)malloc(g->stack_cap);
74
+ }
75
+
76
+ void pln_gen_free(pln_gen_t *g) {
77
+ free(g->buf); g->buf = NULL;
78
+ free(g->stack); g->stack = NULL;
79
+ g->len = g->cap = g->stack_len = g->stack_cap = 0;
80
+ }
81
+
82
+ static inline void g_ensure(pln_gen_t *g, int extra) {
83
+ if (g->len + extra + 1 > g->cap) {
84
+ do { g->cap *= 2; } while (g->len + extra + 1 > g->cap);
85
+ g->buf = (char *)pln_realloc(g->buf, g->cap);
86
+ }
87
+ }
88
+
89
+ static void g_write_len(pln_gen_t *g, const char *s, int n) {
90
+ g_ensure(g, n);
91
+ memcpy(g->buf + g->len, s, n);
92
+ g->len += n;
93
+ }
94
+
95
+ static void g_write(pln_gen_t *g, const char *s) {
96
+ g_write_len(g, s, (int)strlen(s));
97
+ }
98
+
99
+ static void g_writec(pln_gen_t *g, char c) {
100
+ g_ensure(g, 1);
101
+ g->buf[g->len++] = c;
102
+ }
103
+
104
+ static inline void g_push(pln_gen_t *g, char c) {
105
+ if (g->stack_len >= g->stack_cap) {
106
+ g->stack_cap *= 2;
107
+ g->stack = (char *)pln_realloc(g->stack, g->stack_cap);
108
+ }
109
+ g->stack[g->stack_len++] = c;
110
+ }
111
+
112
+ static inline char g_top(pln_gen_t *g) {
113
+ return g->stack_len > 0 ? g->stack[g->stack_len - 1] : 0;
114
+ }
115
+
116
+ static void g_flush_pop(pln_gen_t *g) {
117
+ if (g->pending_pop > 0) {
118
+ /* Insert " N" before the last \n (line-end pop suffix).
119
+ * Buffer currently ends with \n from the previous value line. */
120
+ if (g->len > 0 && g->buf[g->len - 1] == '\n') {
121
+ char tmp[16];
122
+ int i = 0, x = g->pending_pop;
123
+ int p = g->len - 1;
124
+ if (x >= 10) { if (x >= 100) { tmp[i++] = '0' + x/100; x %= 100; } tmp[i++] = '0' + x/10; x %= 10; }
125
+ tmp[i++] = '0' + x;
126
+ g_ensure(g, i + 1);
127
+ memmove(g->buf + p + i + 1, g->buf + p, 1);
128
+ g->buf[p] = ' ';
129
+ memcpy(g->buf + p + 1, tmp, i);
130
+ g->len += i + 1;
131
+ }
132
+ g->pending_pop = 0;
133
+ }
134
+ }
135
+
136
+ static void g_start_container(pln_gen_t *g, const char *ch, char typ) {
137
+ if (g->stack_len > 0 && g_top(g) == 'o' && g->awaiting_value) {
138
+ g_write_len(g, ch, 1);
139
+ g->awaiting_value = 0;
140
+ } else {
141
+ g_flush_pop(g);
142
+ g_write_len(g, ch, 1);
143
+ }
144
+ g_writec(g, '\n');
145
+ g_push(g, typ);
146
+ (void)g;
147
+ g->awaiting_value = 0;
148
+ }
149
+
150
+ void pln_gen_begin_object(pln_gen_t *g) { g_start_container(g, "{", 'o'); }
151
+ void pln_gen_begin_array(pln_gen_t *g) { g_start_container(g, "[", 'a'); }
152
+
153
+ void pln_gen_end_object(pln_gen_t *g) {
154
+ if (g_top(g) != 'o') { fprintf(stderr, "end_object mismatch\n"); abort(); }
155
+ g->stack_len--;
156
+ g->pending_pop++;
157
+ }
158
+
159
+ void pln_gen_end_array(pln_gen_t *g) {
160
+ if (g_top(g) != 'a') { fprintf(stderr, "end_array mismatch\n"); abort(); }
161
+ g->stack_len--;
162
+ g->pending_pop++;
163
+ }
164
+
165
+ void pln_gen_key(pln_gen_t *g, const char *k) {
166
+ g_flush_pop(g);
167
+ g_write(g, k);
168
+ g_write_len(g, ": ", 2);
169
+ g->awaiting_value = 1;
170
+ }
171
+
172
+ static void g_put_value_len(pln_gen_t *g, const char *s, int n) {
173
+ g->has_leaf_value = 1;
174
+ if (g_top(g) == 'o') {
175
+ g->awaiting_value = 0;
176
+ g_write_len(g, s, n);
177
+ g_flush_pop(g);
178
+ g_writec(g, '\n');
179
+ } else {
180
+ g_write_len(g, s, n);
181
+ g_flush_pop(g);
182
+ g_writec(g, '\n');
183
+ }
184
+ }
185
+
186
+ void pln_gen_value_null(pln_gen_t *g) { g_put_value_len(g, "null", 4); }
187
+ void pln_gen_value_bool(pln_gen_t *g, int v) { g_put_value_len(g, v ? "true" : "false", v ? 4 : 5); }
188
+ void pln_gen_value_int(pln_gen_t *g, long long v) {
189
+ char tmp[32]; int pos = 0;
190
+ if (v < 0) { tmp[pos++] = '-'; v = -v; }
191
+ if (v == 0) { tmp[pos++] = '0'; }
192
+ else {
193
+ char rev[24]; int rpos = 0;
194
+ while (v) { rev[rpos++] = '0' + (v % 10); v /= 10; }
195
+ while (rpos) tmp[pos++] = rev[--rpos];
196
+ }
197
+ g_put_value_len(g, tmp, pos);
198
+ }
199
+ void pln_gen_value_float(pln_gen_t *g, double v) {
200
+ char tmp[64]; int pos = snprintf(tmp, sizeof(tmp), "%.15g", v);
201
+ g_put_value_len(g, tmp, pos);
202
+ }
203
+ void pln_gen_value_string(pln_gen_t *g, const char *v) {
204
+ if (g_top(g) == 'o') {
205
+ g->awaiting_value = 0;
206
+ }
207
+
208
+ /* 直接写入缓冲:先快速扫描是否需要转义 */
209
+ int has_quote = 0, n = (int)strlen(v);
210
+ for (int i = 0; i < n; i++) { if (v[i] == '"') { has_quote = 1; break; } }
211
+
212
+ if (!has_quote) {
213
+ /* 常见路径:无转义,一次写入 */
214
+ g_ensure(g, n + 16);
215
+ g->buf[g->len++] = '"';
216
+ memcpy(g->buf + g->len, v, n); g->len += n;
217
+ g->buf[g->len++] = '"';
218
+ g_flush_pop(g);
219
+ g->buf[g->len++] = '\n';
220
+ } else {
221
+ /* 罕见路径:有 "",逐字符写入(无额外分配) */
222
+ int est = n + (n / 8) + 16;
223
+ g_ensure(g, est);
224
+ g->buf[g->len++] = '"';
225
+ for (int i = 0; i < n; i++) {
226
+ g->buf[g->len++] = v[i];
227
+ if (v[i] == '"') g->buf[g->len++] = '"';
228
+ }
229
+ g->buf[g->len++] = '"';
230
+ g_flush_pop(g);
231
+ g->buf[g->len++] = '\n';
232
+ }
233
+ }
234
+
235
+ const char *pln_gen_getvalue(pln_gen_t *g) {
236
+ g_ensure(g, 1);
237
+ g->buf[g->len] = '\0';
238
+ return g->buf;
239
+ }
240
+
241
+ /* ─── DOM 值 ──────────────────────────────────────────────── */
242
+
243
+ pln_value_t *pln_value_new(pln_value_type_t t) {
244
+ pln_value_t *v = (pln_value_t *)malloc(sizeof(pln_value_t));
245
+ v->type = t;
246
+ v->data.int_val = 0;
247
+ v->child = NULL;
248
+ v->next = NULL;
249
+ v->key = NULL;
250
+ return v;
251
+ }
252
+
253
+ pln_value_t *pln_value_new_object(void) { return pln_value_new(PLN_OBJECT); }
254
+ pln_value_t *pln_value_new_array(void) { return pln_value_new(PLN_ARRAY); }
255
+ pln_value_t *pln_value_new_null(void) { return pln_value_new(PLN_NULL); }
256
+
257
+ pln_value_t *pln_value_new_bool(int b) {
258
+ pln_value_t *v = pln_value_new(PLN_BOOL);
259
+ v->data.bool_val = b ? 1 : 0;
260
+ return v;
261
+ }
262
+
263
+ pln_value_t *pln_value_new_int(long long i) {
264
+ pln_value_t *v = pln_value_new(PLN_INT);
265
+ v->data.int_val = i;
266
+ return v;
267
+ }
268
+
269
+ pln_value_t *pln_value_new_float(double d) {
270
+ pln_value_t *v = pln_value_new(PLN_FLOAT);
271
+ v->data.float_val = d;
272
+ return v;
273
+ }
274
+
275
+ pln_value_t *pln_value_new_string(const char *s) {
276
+ pln_value_t *v = pln_value_new(PLN_STRING);
277
+ v->data.string_val = pln_strdup(s);
278
+ return v;
279
+ }
280
+
281
+ pln_value_t *pln_value_new_string_len(const char *s, int len) {
282
+ pln_value_t *v = pln_value_new(PLN_STRING);
283
+ v->data.string_val = pln_strdupl(s, len);
284
+ return v;
285
+ }
286
+
287
+ void pln_value_add_to_object(pln_value_t *obj, const char *key, pln_value_t *val) {
288
+ val->key = pln_strdup(key);
289
+ if (!obj->child) { obj->child = val; }
290
+ else {
291
+ pln_value_t *sib = obj->child;
292
+ while (sib->next) sib = sib->next;
293
+ sib->next = val;
294
+ }
295
+ }
296
+
297
+ void pln_value_add_to_object_nocopy(pln_value_t *obj, char *key, pln_value_t *val) {
298
+ val->key = key; /* 接管所有权 */
299
+ if (!obj->child) { obj->child = val; }
300
+ else {
301
+ pln_value_t *sib = obj->child;
302
+ while (sib->next) sib = sib->next;
303
+ sib->next = val;
304
+ }
305
+ }
306
+
307
+ void pln_value_add_to_array(pln_value_t *arr, pln_value_t *val) {
308
+ if (!arr->child) { arr->child = val; }
309
+ else {
310
+ pln_value_t *sib = arr->child;
311
+ while (sib->next) sib = sib->next;
312
+ sib->next = val;
313
+ }
314
+ }
315
+
316
+ void pln_value_free(pln_value_t *v) {
317
+ if (!v) return;
318
+ pln_value_free(v->child);
319
+ pln_value_free(v->next);
320
+ free(v->key);
321
+ if (v->type == PLN_STRING) free(v->data.string_val);
322
+ free(v);
323
+ }
324
+
325
+ /* ─── DOM 序列化 ──────────────────────────────────────────── */
326
+
327
+ static void pln_write_value(pln_gen_t *g, pln_value_t *v) {
328
+ pln_value_t *c;
329
+ switch (v->type) {
330
+ case PLN_OBJECT:
331
+ pln_gen_begin_object(g);
332
+ for (c = v->child; c; c = c->next) {
333
+ if (c->key) pln_gen_key(g, c->key);
334
+ pln_write_value(g, c);
335
+ }
336
+ pln_gen_end_object(g);
337
+ break;
338
+ case PLN_ARRAY:
339
+ pln_gen_begin_array(g);
340
+ for (c = v->child; c; c = c->next) pln_write_value(g, c);
341
+ pln_gen_end_array(g);
342
+ break;
343
+ case PLN_NULL: pln_gen_value_null(g); break;
344
+ case PLN_BOOL: pln_gen_value_bool(g, v->data.bool_val); break;
345
+ case PLN_INT: pln_gen_value_int(g, v->data.int_val); break;
346
+ case PLN_FLOAT: pln_gen_value_float(g, v->data.float_val); break;
347
+ case PLN_STRING: pln_gen_value_string(g, v->data.string_val); break;
348
+ }
349
+ }
350
+
351
+ char *pln_dumps(pln_value_t *v) {
352
+ pln_gen_t g;
353
+ pln_gen_init(&g);
354
+ pln_write_value(&g, v);
355
+ /* Flush remaining pop: all containers including root close explicitly */
356
+ if (g.pending_pop > 0 && g.has_leaf_value) g_flush_pop(&g);
357
+ const char *s = pln_gen_getvalue(&g);
358
+ char *result = pln_strdup(s);
359
+ pln_gen_free(&g);
360
+ return result;
361
+ }
362
+
363
+
364
+ /* ─── 打印 ────────────────────────────────────────────────── */
365
+
366
+ static void pln_print_internal(pln_value_t *v, int depth) {
367
+ if (!v) { printf("null\n"); return; }
368
+ pln_value_t *c;
369
+ for (int i = 0; i < depth; i++) printf(" ");
370
+ if (v->key) printf("\"%s\": ", v->key);
371
+ switch (v->type) {
372
+ case PLN_OBJECT:
373
+ printf("{\n");
374
+ for (c = v->child; c; c = c->next) pln_print_internal(c, depth + 1);
375
+ for (int i = 0; i < depth; i++) printf(" ");
376
+ printf("}\n");
377
+ break;
378
+ case PLN_ARRAY:
379
+ printf("[\n");
380
+ for (c = v->child; c; c = c->next) pln_print_internal(c, depth + 1);
381
+ for (int i = 0; i < depth; i++) printf(" ");
382
+ printf("]\n");
383
+ break;
384
+ case PLN_NULL: printf("null\n"); break;
385
+ case PLN_BOOL: printf("%s\n", v->data.bool_val ? "true" : "false"); break;
386
+ case PLN_INT: printf("%lld\n", v->data.int_val); break;
387
+ case PLN_FLOAT: printf("%.17g\n", v->data.float_val); break;
388
+ case PLN_STRING: printf("\"%s\"\n", v->data.string_val); break;
389
+ }
390
+ }
391
+
392
+ void pln_print(pln_value_t *v) { pln_print_internal(v, 0); }
@@ -0,0 +1,96 @@
1
+ /* popline.h — PopLine v2 C reference implementation */
2
+
3
+ #ifndef POPLINE_H
4
+ #define POPLINE_H
5
+
6
+ #include <stddef.h>
7
+
8
+ #ifdef __cplusplus
9
+ extern "C" {
10
+ #endif
11
+
12
+ /* ─── 值类型 ─────────────────────────────────────────────── */
13
+
14
+ typedef enum {
15
+ PLN_NULL,
16
+ PLN_BOOL,
17
+ PLN_INT,
18
+ PLN_FLOAT,
19
+ PLN_STRING,
20
+ PLN_OBJECT,
21
+ PLN_ARRAY
22
+ } pln_value_type_t;
23
+
24
+ /* ─── DOM 值 ───────────────────────────────────────────── */
25
+
26
+ typedef struct pln_value_t {
27
+ pln_value_type_t type;
28
+ union {
29
+ int bool_val; /* 0 or 1 */
30
+ long long int_val;
31
+ double float_val;
32
+ char *string_val;
33
+ } data;
34
+ /* for object/array */
35
+ struct pln_value_t *child; /* first child / first element */
36
+ struct pln_value_t *next; /* next sibling */
37
+ char *key; /* key name if part of object */
38
+ } pln_value_t;
39
+
40
+ /* ─── 生成器 ───────────────────────────────────────────── */
41
+
42
+ typedef struct {
43
+ char *buf;
44
+ int len;
45
+ int cap;
46
+ char *stack;
47
+ int stack_len;
48
+ int stack_cap;
49
+ int pending_pop;
50
+ int awaiting_value;
51
+ int has_leaf_value;
52
+ } pln_gen_t;
53
+
54
+ void pln_gen_init(pln_gen_t *g);
55
+ void pln_gen_begin_object(pln_gen_t *g);
56
+ void pln_gen_end_object(pln_gen_t *g);
57
+ void pln_gen_begin_array(pln_gen_t *g);
58
+ void pln_gen_end_array(pln_gen_t *g);
59
+ void pln_gen_key(pln_gen_t *g, const char *k);
60
+ void pln_gen_value_null(pln_gen_t *g);
61
+ void pln_gen_value_bool(pln_gen_t *g, int v);
62
+ void pln_gen_value_int(pln_gen_t *g, long long v);
63
+ void pln_gen_value_float(pln_gen_t *g, double v);
64
+ void pln_gen_value_string(pln_gen_t *g, const char *v);
65
+ const char *pln_gen_getvalue(pln_gen_t *g);
66
+ void pln_gen_free(pln_gen_t *g);
67
+
68
+ /* ─── DOM 构建 ─────────────────────────────────────────── */
69
+
70
+ pln_value_t *pln_value_new_object(void);
71
+ pln_value_t *pln_value_new_array(void);
72
+ pln_value_t *pln_value_new_string(const char *s);
73
+ pln_value_t *pln_value_new_string_len(const char *s, int len);
74
+ pln_value_t *pln_value_new_int(long long v);
75
+ pln_value_t *pln_value_new_float(double v);
76
+ pln_value_t *pln_value_new_bool(int v);
77
+ pln_value_t *pln_value_new_null(void);
78
+ void pln_value_add_to_object(pln_value_t *obj, const char *key, pln_value_t *val);
79
+ void pln_value_add_to_object_nocopy(pln_value_t *obj, char *key, pln_value_t *val); /* 接管 key 所有权 */
80
+ void pln_value_add_to_array(pln_value_t *arr, pln_value_t *val);
81
+ void pln_value_free(pln_value_t *v);
82
+
83
+ /* ─── 解析 / 序列化 ──────────────────────────────────── */
84
+
85
+ pln_value_t *pln_loads(const char *text); /* 解析 PopLine 文本 → DOM 树 */
86
+ char *pln_dumps(pln_value_t *v); /* DOM 树 → PopLine 文本 */
87
+
88
+ /* ─── 辅助 ─────────────────────────────────────────────── */
89
+
90
+ void pln_print(pln_value_t *v);
91
+
92
+ #ifdef __cplusplus
93
+ }
94
+ #endif
95
+
96
+ #endif /* POPLINE_H */