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.
- popline_py-0.4.0/MANIFEST.in +1 -0
- popline_py-0.4.0/PKG-INFO +73 -0
- popline_py-0.4.0/README.md +43 -0
- popline_py-0.4.0/popline.c +392 -0
- popline_py-0.4.0/popline.h +96 -0
- popline_py-0.4.0/popline_module.c +843 -0
- popline_py-0.4.0/popline_parser.c +378 -0
- popline_py-0.4.0/popline_py.egg-info/PKG-INFO +73 -0
- popline_py-0.4.0/popline_py.egg-info/SOURCES.txt +11 -0
- popline_py-0.4.0/popline_py.egg-info/dependency_links.txt +1 -0
- popline_py-0.4.0/popline_py.egg-info/top_level.txt +1 -0
- popline_py-0.4.0/setup.cfg +4 -0
- popline_py-0.4.0/setup.py +47 -0
|
@@ -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 */
|