fsonl 0.1.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.
fsonl-0.1.0/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .pytest_cache/
7
+ .omc/
8
+ .omx/
fsonl-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FSONL Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
fsonl-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.4
2
+ Name: fsonl
3
+ Version: 0.1.0
4
+ Summary: Function-Styled Object Notation Lines — a line-based serialization format with typed schemas
5
+ Project-URL: Homepage, https://github.com/fsonl/fsonl
6
+ Project-URL: Documentation, https://github.com/fsonl/fsonl-py
7
+ Project-URL: Repository, https://github.com/fsonl/fsonl-py
8
+ Author: Hve
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: fsonl,jsonl,schema,serialization
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: Text Processing :: General
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+
27
+ # FSONL
28
+
29
+ **Function-Styled Object Notation Lines**
30
+
31
+ A line-based serialization format where each record's type is immediately visible at the start of the line.
32
+
33
+ ```
34
+ @schema rm(target: string, --force?: bool = false)
35
+ rm("tmp.log", force=true)
36
+ rm("/var/cache", force=false)
37
+ log("info", "server started")
38
+ ```
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ pip install fsonl
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ### Parse with inline schema
49
+
50
+ ```python
51
+ import fsonl
52
+
53
+ text = """
54
+ @schema rm(target: string, --force?: bool = false)
55
+ rm("tmp.log")
56
+ rm("/var/cache", force=true)
57
+ """
58
+
59
+ result = fsonl.loads(text)
60
+ for entry in result.entries:
61
+ print(entry)
62
+ # {'type': 'rm', 'target': 'tmp.log', 'force': False}
63
+ # {'type': 'rm', 'target': '/var/cache', 'force': True}
64
+ ```
65
+
66
+ ### Parse with code schema
67
+
68
+ ```python
69
+ import fsonl
70
+
71
+ schema = fsonl.Schema.from_string(
72
+ "@schema log(level: string, msg: string)"
73
+ )
74
+
75
+ result = fsonl.loads('log("info", "started")\n', schema=schema)
76
+ print(result.entries[0])
77
+ # {'type': 'log', 'level': 'info', 'msg': 'started'}
78
+ ```
79
+
80
+ ### Define schema from Python functions (3.10+)
81
+
82
+ ```python
83
+ import fsonl
84
+
85
+ schema = fsonl.Schema()
86
+
87
+ @schema.define
88
+ def rm(target: str, *, force: bool = False): ...
89
+
90
+ result = fsonl.loads('rm("tmp.log", force=true)\n', schema=schema)
91
+ print(result.entries[0])
92
+ # {'type': 'rm', 'target': 'tmp.log', 'force': True}
93
+ ```
94
+
95
+ ### Serialize
96
+
97
+ ```python
98
+ import fsonl
99
+
100
+ schema = fsonl.Schema.from_string(
101
+ "@schema log(level: string, msg: string)"
102
+ )
103
+
104
+ print(fsonl.dumps({"type": "log", "level": "info", "msg": "hello"}, schema=schema))
105
+ # @schema log(level: string, msg: string)
106
+ # log("info", "hello")
107
+ ```
108
+
109
+ ### Stream from file
110
+
111
+ ```python
112
+ import fsonl
113
+
114
+ with open("events.fsonl", newline="") as f:
115
+ for entry in fsonl.iter_entries(f):
116
+ print(entry)
117
+ ```
118
+
119
+ ### Raw mode (no schema binding)
120
+
121
+ ```python
122
+ import fsonl
123
+
124
+ result = fsonl.loads_raw('x(1, "hello", flag=true)\n')
125
+ entry = result[0]
126
+ print(entry["type"]) # 'x'
127
+ print(entry["positional"]) # [1, 'hello']
128
+ print(entry["named"]) # {'flag': True}
129
+ ```
130
+
131
+ ## API
132
+
133
+ ### Parsing
134
+
135
+ | Function | Description |
136
+ |----------|-------------|
137
+ | `loads(text, *, schema, ignore_inline_schema, extra_fields)` | Parse FSONL text with schema binding |
138
+ | `load(fp, **kwargs)` | Parse from file object with schema binding |
139
+ | `loads_raw(text)` | Parse FSONL text without binding (Stage 1 only) |
140
+ | `load_raw(fp)` | Parse from file object without binding (Stage 1 only) |
141
+ | `iter_entries(source, *, schema, ignore_inline_schema, extra_fields)` | Lazy iterator over bound entries |
142
+ | `iter_raw(source)` | Lazy iterator over raw entries |
143
+ | `bind(entry, schema, *, extra_fields)` | Bind a single raw dict to a Schema |
144
+
145
+ ### Serialization
146
+
147
+ | Function | Description |
148
+ |----------|-------------|
149
+ | `dumps(entries, *, schema, allow_extra, exclude_schema)` | Serialize to FSONL text |
150
+ | `dump(entries, fp, *, schema, allow_extra, exclude_schema)` | Serialize to file object |
151
+
152
+ ### Schema
153
+
154
+ | Method | Description |
155
+ |--------|-------------|
156
+ | `Schema.from_string(text)` | Create from `@schema` lines |
157
+ | `Schema.from_fsonl(text)` | Extract `@schema` from FSONL text (non-schema lines ignored) |
158
+ | `Schema.from_file(path)` | Load `@schema` from a `.fsonl` file |
159
+ | `@schema.define` | Decorator: define schema from function signature (Python 3.10+) |
160
+ | `schema.add(text)` | Add more `@schema` definitions |
161
+ | `schema.get(type_name)` | Look up a type definition |
162
+ | `schema.has(type_name)` | Check if a type is defined |
163
+ | `schema.type_names()` | List all defined type names |
164
+
165
+ ### Options
166
+
167
+ | Option | Default | Description |
168
+ |--------|---------|-------------|
169
+ | `ignore_inline_schema` | `False` | Skip `@schema` directives in the content |
170
+ | `allow_extra` | `False` | (`dumps` only) Ignore extra keys not in schema |
171
+ | `exclude_schema` | `False` | (`dumps` only) Omit `@schema` lines from output |
172
+ | `extra_fields` | `ExtraFieldPolicy.ERROR` | Policy for undeclared named arguments |
173
+
174
+ ### Errors
175
+
176
+ All errors include line numbers: `str(error)` produces `"line 42: message"`.
177
+
178
+ | Exception | Kind | Stage |
179
+ |-----------|------|-------|
180
+ | `ParseError` | `syntax_error` | Stage 1 (syntax parse) |
181
+ | `SchemaError` | `schema_error` | Schema definition / cross-validation |
182
+ | `BindError` | `bind_error` | Stage 2 (data vs schema mismatch) |
183
+
184
+ All inherit from `FsonlError`, which inherits from `Exception`.
185
+
186
+ ## Schema Types
187
+
188
+ ```
189
+ string -- JSON string
190
+ number -- JSON number (int or float)
191
+ bool -- true / false
192
+ null -- null
193
+ any -- any JSON value
194
+ string[] -- array of strings
195
+ (string | number)[] -- array of union
196
+ { cmd: string, id?: number } -- fixed-shape object
197
+ string | null -- nullable string
198
+ ```
199
+
200
+ ## CLI
201
+
202
+ ```bash
203
+ # Parse with schema binding (default)
204
+ echo '@schema x(a: number)\nx(1)' | python -m fsonl parse
205
+
206
+ # Raw parse (no binding)
207
+ echo 'x(1)' | python -m fsonl parse --raw
208
+
209
+ # Unknown types as raw dict
210
+ echo 'x(1)' | python -m fsonl parse --allow-unknown
211
+
212
+ # Extract @schema directives only
213
+ echo '@schema x(a: number)\nx(1)' | python -m fsonl parse --schema
214
+ ```
215
+
216
+ ## Format Overview
217
+
218
+ - One entry per line: `type(args)`
219
+ - Values are JSON literals: strings, numbers, booleans, null, arrays, objects
220
+ - Positional args come before named args: `log("info", tag="v2")`
221
+ - Comments: `// ...` (outside argument lists only)
222
+ - File extension: `.fsonl`, MIME: `text/fsonl`, encoding: UTF-8
223
+
224
+ ## Specification
225
+
226
+ - [SPEC.ko.md](https://github.com/fsonl/fsonl/blob/main/spec/SPEC.ko.md) -- Language specification (Korean)
227
+ - [GRAMMAR.ko.peg](https://github.com/fsonl/fsonl/blob/main/spec/GRAMMAR.ko.peg) -- PEG formal grammar (Korean)
228
+
229
+ ## License
230
+
231
+ MIT
@@ -0,0 +1,220 @@
1
+ # FSONL
2
+
3
+ **Function-Styled Object Notation Lines**
4
+
5
+ 각 줄의 레코드 타입이 선두에 즉시 드러나는 줄 단위 직렬화 포맷.
6
+
7
+ ```
8
+ @schema rm(target: string, --force?: bool = false)
9
+ rm("tmp.log", force=true)
10
+ rm("/var/cache", force=false)
11
+ log("info", "server started")
12
+ ```
13
+
14
+ ## 설치
15
+
16
+ ```bash
17
+ pip install fsonl
18
+ ```
19
+
20
+ ## 빠른 시작
21
+
22
+ ### 인라인 스키마로 파싱
23
+
24
+ ```python
25
+ import fsonl
26
+
27
+ text = """
28
+ @schema rm(target: string, --force?: bool = false)
29
+ rm("tmp.log")
30
+ rm("/var/cache", force=true)
31
+ """
32
+
33
+ result = fsonl.loads(text)
34
+ for entry in result.entries:
35
+ print(entry)
36
+ # {'type': 'rm', 'target': 'tmp.log', 'force': False}
37
+ # {'type': 'rm', 'target': '/var/cache', 'force': True}
38
+ ```
39
+
40
+ ### 코드 스키마로 파싱
41
+
42
+ ```python
43
+ import fsonl
44
+
45
+ schema = fsonl.Schema.from_string(
46
+ "@schema log(level: string, msg: string)"
47
+ )
48
+
49
+ result = fsonl.loads('log("info", "started")\n', schema=schema)
50
+ print(result.entries[0])
51
+ # {'type': 'log', 'level': 'info', 'msg': 'started'}
52
+ ```
53
+
54
+ ### Python 함수로 스키마 정의 (3.10+)
55
+
56
+ ```python
57
+ import fsonl
58
+
59
+ schema = fsonl.Schema()
60
+
61
+ @schema.define
62
+ def rm(target: str, *, force: bool = False): ...
63
+
64
+ result = fsonl.loads('rm("tmp.log", force=true)\n', schema=schema)
65
+ print(result.entries[0])
66
+ # {'type': 'rm', 'target': 'tmp.log', 'force': True}
67
+ ```
68
+
69
+ ### 직렬화
70
+
71
+ ```python
72
+ import fsonl
73
+
74
+ schema = fsonl.Schema.from_string(
75
+ "@schema log(level: string, msg: string)"
76
+ )
77
+
78
+ print(fsonl.dumps({"type": "log", "level": "info", "msg": "hello"}, schema=schema))
79
+ # @schema log(level: string, msg: string)
80
+ # log("info", "hello")
81
+ ```
82
+
83
+ ### 파일 스트리밍
84
+
85
+ ```python
86
+ import fsonl
87
+
88
+ with open("events.fsonl", newline="") as f:
89
+ for entry in fsonl.iter_entries(f):
90
+ print(entry)
91
+ ```
92
+
93
+ ### Raw 모드 (스키마 바인딩 없이)
94
+
95
+ ```python
96
+ import fsonl
97
+
98
+ result = fsonl.loads_raw('x(1, "hello", flag=true)\n')
99
+ entry = result[0]
100
+ print(entry["type"]) # 'x'
101
+ print(entry["positional"]) # [1, 'hello']
102
+ print(entry["named"]) # {'flag': True}
103
+ ```
104
+
105
+ ## API
106
+
107
+ ### 파싱
108
+
109
+ | 함수 | 설명 |
110
+ |------|------|
111
+ | `loads(text, *, schema, ignore_inline_schema, extra_fields)` | FSONL 텍스트 스키마 바인딩 파싱 |
112
+ | `load(fp, **kwargs)` | 파일 객체에서 스키마 바인딩 파싱 |
113
+ | `loads_raw(text)` | FSONL 텍스트 바인딩 없이 파싱 (Stage 1만) |
114
+ | `load_raw(fp)` | 파일 객체에서 바인딩 없이 파싱 (Stage 1만) |
115
+ | `iter_entries(source, *, schema, ignore_inline_schema, extra_fields)` | 바인딩된 엔트리 지연 이터레이터 |
116
+ | `iter_raw(source)` | Raw 엔트리 지연 이터레이터 |
117
+ | `bind(entry, schema, *, extra_fields)` | 단일 raw dict를 Schema에 바인딩 |
118
+
119
+ ### 직렬화
120
+
121
+ | 함수 | 설명 |
122
+ |------|------|
123
+ | `dumps(entries, *, schema, allow_extra, exclude_schema)` | FSONL 텍스트로 직렬화 |
124
+ | `dump(entries, fp, *, schema, allow_extra, exclude_schema)` | 파일 객체에 직렬화 |
125
+
126
+ ### 스키마
127
+
128
+ | 메서드 | 설명 |
129
+ |--------|------|
130
+ | `Schema.from_string(text)` | `@schema` 문자열로 생성 |
131
+ | `Schema.from_fsonl(text)` | FSONL 텍스트에서 `@schema` 추출 (비스키마 줄 무시) |
132
+ | `Schema.from_file(path)` | `.fsonl` 파일에서 `@schema` 로드 |
133
+ | `@schema.define` | 데코레이터: 함수 시그니처로 스키마 정의 (Python 3.10+) |
134
+ | `schema.add(text)` | `@schema` 정의 추가 |
135
+ | `schema.get(type_name)` | 타입 정의 조회 |
136
+ | `schema.has(type_name)` | 타입 정의 존재 여부 확인 |
137
+ | `schema.type_names()` | 정의된 모든 타입명 목록 |
138
+
139
+ ### 옵션
140
+
141
+ | 옵션 | 기본값 | 설명 |
142
+ |------|--------|------|
143
+ | `ignore_inline_schema` | `False` | 콘텐츠 내 `@schema` 디렉티브 무시 |
144
+ | `allow_extra` | `False` | (`dumps` 전용) 스키마에 없는 추가 키 허용 |
145
+ | `exclude_schema` | `False` | (`dumps` 전용) 출력에서 `@schema` 줄 제외 |
146
+ | `extra_fields` | `ExtraFieldPolicy.ERROR` | 미선언 named arg 처리 정책 |
147
+
148
+ ### ExtraFieldPolicy
149
+
150
+ ```python
151
+ from fsonl import ExtraFieldPolicy
152
+
153
+ # 미선언 필드가 있으면 에러 (기본값)
154
+ result = fsonl.loads(text, schema=schema, extra_fields=ExtraFieldPolicy.ERROR)
155
+
156
+ # 미선언 필드 보존
157
+ result = fsonl.loads(text, schema=schema, extra_fields=ExtraFieldPolicy.PRESERVE)
158
+
159
+ # 미선언 필드 조용히 제거
160
+ result = fsonl.loads(text, schema=schema, extra_fields=ExtraFieldPolicy.STRIP)
161
+ ```
162
+
163
+ ### 에러
164
+
165
+ 모든 에러에 줄 번호 포함: `str(error)`는 `"line 42: message"` 형태.
166
+
167
+ | 예외 | 종류 | 단계 |
168
+ |------|------|------|
169
+ | `ParseError` | `syntax_error` | 1단계 (문법 파싱) |
170
+ | `SchemaError` | `schema_error` | 스키마 정의 / 교차 검증 |
171
+ | `BindError` | `bind_error` | 2단계 (데이터와 스키마 불일치) |
172
+
173
+ 모두 `FsonlError`를 상속하며, `FsonlError`는 `Exception`을 상속.
174
+
175
+ ## 스키마 타입
176
+
177
+ ```
178
+ string -- JSON 문자열
179
+ number -- JSON 숫자 (정수 또는 실수)
180
+ bool -- true / false
181
+ null -- null
182
+ any -- 모든 JSON 값
183
+ string[] -- 문자열 배열
184
+ (string | number)[] -- 유니온 배열
185
+ { cmd: string, id?: number } -- 고정 구조 객체
186
+ string | null -- nullable 문자열
187
+ ```
188
+
189
+ ## CLI
190
+
191
+ ```bash
192
+ # 스키마 바인딩 파싱 (기본)
193
+ echo '@schema x(a: number)\nx(1)' | python -m fsonl parse
194
+
195
+ # Raw 파싱 (바인딩 없음)
196
+ echo 'x(1)' | python -m fsonl parse --raw
197
+
198
+ # 미정의 타입을 raw dict로
199
+ echo 'x(1)' | python -m fsonl parse --allow-unknown
200
+
201
+ # @schema 디렉티브만 추출
202
+ echo '@schema x(a: number)\nx(1)' | python -m fsonl parse --schema
203
+ ```
204
+
205
+ ## 포맷 개요
206
+
207
+ - 한 줄에 하나의 엔트리: `type(args)`
208
+ - 값은 JSON 리터럴: 문자열, 숫자, 불리언, null, 배열, 객체
209
+ - positional 인자가 named 인자보다 앞: `log("info", tag="v2")`
210
+ - 주석: `// ...` (인자 목록 바깥에서만)
211
+ - 파일 확장자: `.fsonl`, MIME: `text/fsonl`, 인코딩: UTF-8
212
+
213
+ ## 스펙
214
+
215
+ - [SPEC.ko.md](https://github.com/fsonl/fsonl/blob/main/spec/SPEC.ko.md) -- 언어 스펙
216
+ - [GRAMMAR.ko.peg](https://github.com/fsonl/fsonl/blob/main/spec/GRAMMAR.ko.peg) -- PEG 형식 문법
217
+
218
+ ## 라이선스
219
+
220
+ MIT
fsonl-0.1.0/README.md ADDED
@@ -0,0 +1,205 @@
1
+ # FSONL
2
+
3
+ **Function-Styled Object Notation Lines**
4
+
5
+ A line-based serialization format where each record's type is immediately visible at the start of the line.
6
+
7
+ ```
8
+ @schema rm(target: string, --force?: bool = false)
9
+ rm("tmp.log", force=true)
10
+ rm("/var/cache", force=false)
11
+ log("info", "server started")
12
+ ```
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pip install fsonl
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ### Parse with inline schema
23
+
24
+ ```python
25
+ import fsonl
26
+
27
+ text = """
28
+ @schema rm(target: string, --force?: bool = false)
29
+ rm("tmp.log")
30
+ rm("/var/cache", force=true)
31
+ """
32
+
33
+ result = fsonl.loads(text)
34
+ for entry in result.entries:
35
+ print(entry)
36
+ # {'type': 'rm', 'target': 'tmp.log', 'force': False}
37
+ # {'type': 'rm', 'target': '/var/cache', 'force': True}
38
+ ```
39
+
40
+ ### Parse with code schema
41
+
42
+ ```python
43
+ import fsonl
44
+
45
+ schema = fsonl.Schema.from_string(
46
+ "@schema log(level: string, msg: string)"
47
+ )
48
+
49
+ result = fsonl.loads('log("info", "started")\n', schema=schema)
50
+ print(result.entries[0])
51
+ # {'type': 'log', 'level': 'info', 'msg': 'started'}
52
+ ```
53
+
54
+ ### Define schema from Python functions (3.10+)
55
+
56
+ ```python
57
+ import fsonl
58
+
59
+ schema = fsonl.Schema()
60
+
61
+ @schema.define
62
+ def rm(target: str, *, force: bool = False): ...
63
+
64
+ result = fsonl.loads('rm("tmp.log", force=true)\n', schema=schema)
65
+ print(result.entries[0])
66
+ # {'type': 'rm', 'target': 'tmp.log', 'force': True}
67
+ ```
68
+
69
+ ### Serialize
70
+
71
+ ```python
72
+ import fsonl
73
+
74
+ schema = fsonl.Schema.from_string(
75
+ "@schema log(level: string, msg: string)"
76
+ )
77
+
78
+ print(fsonl.dumps({"type": "log", "level": "info", "msg": "hello"}, schema=schema))
79
+ # @schema log(level: string, msg: string)
80
+ # log("info", "hello")
81
+ ```
82
+
83
+ ### Stream from file
84
+
85
+ ```python
86
+ import fsonl
87
+
88
+ with open("events.fsonl", newline="") as f:
89
+ for entry in fsonl.iter_entries(f):
90
+ print(entry)
91
+ ```
92
+
93
+ ### Raw mode (no schema binding)
94
+
95
+ ```python
96
+ import fsonl
97
+
98
+ result = fsonl.loads_raw('x(1, "hello", flag=true)\n')
99
+ entry = result[0]
100
+ print(entry["type"]) # 'x'
101
+ print(entry["positional"]) # [1, 'hello']
102
+ print(entry["named"]) # {'flag': True}
103
+ ```
104
+
105
+ ## API
106
+
107
+ ### Parsing
108
+
109
+ | Function | Description |
110
+ |----------|-------------|
111
+ | `loads(text, *, schema, ignore_inline_schema, extra_fields)` | Parse FSONL text with schema binding |
112
+ | `load(fp, **kwargs)` | Parse from file object with schema binding |
113
+ | `loads_raw(text)` | Parse FSONL text without binding (Stage 1 only) |
114
+ | `load_raw(fp)` | Parse from file object without binding (Stage 1 only) |
115
+ | `iter_entries(source, *, schema, ignore_inline_schema, extra_fields)` | Lazy iterator over bound entries |
116
+ | `iter_raw(source)` | Lazy iterator over raw entries |
117
+ | `bind(entry, schema, *, extra_fields)` | Bind a single raw dict to a Schema |
118
+
119
+ ### Serialization
120
+
121
+ | Function | Description |
122
+ |----------|-------------|
123
+ | `dumps(entries, *, schema, allow_extra, exclude_schema)` | Serialize to FSONL text |
124
+ | `dump(entries, fp, *, schema, allow_extra, exclude_schema)` | Serialize to file object |
125
+
126
+ ### Schema
127
+
128
+ | Method | Description |
129
+ |--------|-------------|
130
+ | `Schema.from_string(text)` | Create from `@schema` lines |
131
+ | `Schema.from_fsonl(text)` | Extract `@schema` from FSONL text (non-schema lines ignored) |
132
+ | `Schema.from_file(path)` | Load `@schema` from a `.fsonl` file |
133
+ | `@schema.define` | Decorator: define schema from function signature (Python 3.10+) |
134
+ | `schema.add(text)` | Add more `@schema` definitions |
135
+ | `schema.get(type_name)` | Look up a type definition |
136
+ | `schema.has(type_name)` | Check if a type is defined |
137
+ | `schema.type_names()` | List all defined type names |
138
+
139
+ ### Options
140
+
141
+ | Option | Default | Description |
142
+ |--------|---------|-------------|
143
+ | `ignore_inline_schema` | `False` | Skip `@schema` directives in the content |
144
+ | `allow_extra` | `False` | (`dumps` only) Ignore extra keys not in schema |
145
+ | `exclude_schema` | `False` | (`dumps` only) Omit `@schema` lines from output |
146
+ | `extra_fields` | `ExtraFieldPolicy.ERROR` | Policy for undeclared named arguments |
147
+
148
+ ### Errors
149
+
150
+ All errors include line numbers: `str(error)` produces `"line 42: message"`.
151
+
152
+ | Exception | Kind | Stage |
153
+ |-----------|------|-------|
154
+ | `ParseError` | `syntax_error` | Stage 1 (syntax parse) |
155
+ | `SchemaError` | `schema_error` | Schema definition / cross-validation |
156
+ | `BindError` | `bind_error` | Stage 2 (data vs schema mismatch) |
157
+
158
+ All inherit from `FsonlError`, which inherits from `Exception`.
159
+
160
+ ## Schema Types
161
+
162
+ ```
163
+ string -- JSON string
164
+ number -- JSON number (int or float)
165
+ bool -- true / false
166
+ null -- null
167
+ any -- any JSON value
168
+ string[] -- array of strings
169
+ (string | number)[] -- array of union
170
+ { cmd: string, id?: number } -- fixed-shape object
171
+ string | null -- nullable string
172
+ ```
173
+
174
+ ## CLI
175
+
176
+ ```bash
177
+ # Parse with schema binding (default)
178
+ echo '@schema x(a: number)\nx(1)' | python -m fsonl parse
179
+
180
+ # Raw parse (no binding)
181
+ echo 'x(1)' | python -m fsonl parse --raw
182
+
183
+ # Unknown types as raw dict
184
+ echo 'x(1)' | python -m fsonl parse --allow-unknown
185
+
186
+ # Extract @schema directives only
187
+ echo '@schema x(a: number)\nx(1)' | python -m fsonl parse --schema
188
+ ```
189
+
190
+ ## Format Overview
191
+
192
+ - One entry per line: `type(args)`
193
+ - Values are JSON literals: strings, numbers, booleans, null, arrays, objects
194
+ - Positional args come before named args: `log("info", tag="v2")`
195
+ - Comments: `// ...` (outside argument lists only)
196
+ - File extension: `.fsonl`, MIME: `text/fsonl`, encoding: UTF-8
197
+
198
+ ## Specification
199
+
200
+ - [SPEC.ko.md](https://github.com/fsonl/fsonl/blob/main/spec/SPEC.ko.md) -- Language specification (Korean)
201
+ - [GRAMMAR.ko.peg](https://github.com/fsonl/fsonl/blob/main/spec/GRAMMAR.ko.peg) -- PEG formal grammar (Korean)
202
+
203
+ ## License
204
+
205
+ MIT
@@ -0,0 +1,14 @@
1
+ """Parse with inline schema."""
2
+ import fsonl
3
+
4
+ text = """\
5
+ @schema rm(target: string, --force?: bool = false)
6
+ rm("tmp.log")
7
+ rm("/var/cache", force=true)
8
+ """
9
+
10
+ result = fsonl.loads(text)
11
+ for entry in result.entries:
12
+ print(entry)
13
+ # {'type': 'rm', 'target': 'tmp.log', 'force': False}
14
+ # {'type': 'rm', 'target': '/var/cache', 'force': True}