From 14123b738a7b284763c705cb27ddf530935d0c30 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 5 May 2026 21:05:42 +0800 Subject: [PATCH 1/6] docs: add (liii ini-file) to standard library list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5ff165ad..71485724 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ Just like S7 Scheme, [src/goldfish.hpp](src/goldfish.hpp) and [src/goldfish.cpp] | [(liii uuid)](goldfish/liii/uuid.scm) | UUID generation | `uuid4` | | [(liii http)](goldfish/liii/http.scm) | HTTP client library | `http-get`, `http-post`, `http-head` | | [(liii json)](goldfish/liii/json.scm) | JSON parsing and manipulation | `string->json`, `json->string` | +| [(liii ini-file)](goldfish/liii/ini-file.scm) | INI file parsing (SRFI 233) | `make-ini-file-generator`, `make-ini-file-accumulator` | ### SRFI From ced8fa09b93d69d5313fdd4e3050af106c86b56d Mon Sep 17 00:00:00 2001 From: root Date: Tue, 5 May 2026 21:08:41 +0800 Subject: [PATCH 2/6] feat: implement SRFI 233 (INI files) --- goldfish/liii/ini-file.scm | 174 ++++++++++++++++++++ tests/liii/ini-file-test.scm | 303 +++++++++++++++++++++++++++++++++++ 2 files changed, 477 insertions(+) create mode 100644 goldfish/liii/ini-file.scm create mode 100644 tests/liii/ini-file-test.scm diff --git a/goldfish/liii/ini-file.scm b/goldfish/liii/ini-file.scm new file mode 100644 index 00000000..8fa79194 --- /dev/null +++ b/goldfish/liii/ini-file.scm @@ -0,0 +1,174 @@ +;; +;; Copyright (C) 2024 The Goldfish Scheme Authors +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; +;; SRFI 233: INI files +;; Authors: John Cowan (spec), Arvydas Silanskas (reference implementation) + +(define-library (liii ini-file) + (import (scheme base) (liii base)) + (export make-ini-file-generator make-ini-file-accumulator) + (begin + + ;; Whitespace for SRFI 233: space and tab only + (define (ini-whitespace? ch) + (or (char=? ch #\space) (char=? ch #\tab)) + ) ;define + + ;; Find first occurrence of char in string, returns integer index or #f + (define (char-index str ch) + (let ((len (string-length str))) + (let loop + ((i 0)) + (cond ((= i len) #f) + ((char=? (string-ref str i) ch) i) + (else (loop (+ i 1))) + ) ;cond + ) ;let + ) ;let + ) ;define + + ;; Trim leading and trailing whitespace (space/tab) + (define (ini-trim str) + (let ((len (string-length str))) + (let loop-start + ((i 0)) + (if (and (< i len) (ini-whitespace? (string-ref str i))) + (loop-start (+ i 1)) + (let loop-end + ((j (- len 1))) + (if (and (>= j i) (ini-whitespace? (string-ref str j))) + (loop-end (- j 1)) + (substring str i (+ j 1)) + ) ;if + ) ;let + ) ;if + ) ;let + ) ;let + ) ;define + + ;; Remove comment from a line: find first comment-delim and truncate + (define (remove-comment line comment-delim) + (let ((idx (char-index line comment-delim))) + (if idx (substring line 0 idx) line) + ) ;let + ) ;define + + ;; Split string at first occurrence of a character + ;; Returns (before . after) or #f if not found + (define (split-at-first str sep-char) + (let ((idx (char-index str sep-char))) + (if idx + (cons (substring str 0 idx) (substring str (+ idx 1) (string-length str))) + #f + ) ;if + ) ;let + ) ;define + + ;; Parse a section header line like "[name]" + ;; Returns section name string, or #f if not a section header + (define (parse-section-name line) + (let* ((trimmed (ini-trim line)) (len (string-length trimmed))) + (if (and (> len 1) + (char=? (string-ref trimmed 0) #\[) + (char=? (string-ref trimmed (- len 1)) #\]) + ) ;and + (ini-trim (substring trimmed 1 (- len 1))) + #f + ) ;if + ) ;let* + ) ;define + + ;; make-ini-file-generator + ;; Returns a thunk that yields (section key value) lists from the INI port + (define* (make-ini-file-generator inport (key-value-sep #\=) (comment-delim #\;)) + (let ((current-section #f)) + (lambda () + (let loop + () + (let ((line (read-line inport))) + (cond ((eof-object? line) (eof-object)) + (else (let* ((no-comment (remove-comment line comment-delim)) + (trimmed (ini-trim no-comment)) + ) ; + (if (string=? trimmed "") + (loop) + (let ((section-name (parse-section-name trimmed))) + (cond (section-name (set! current-section (string->symbol section-name)) (loop)) + (else (let ((kv (split-at-first trimmed key-value-sep))) + (if kv + (let ((key-str (ini-trim (car kv))) (val-str (ini-trim (cdr kv)))) + (list current-section (string->symbol key-str) val-str) + ) ;let + (list current-section (string->symbol trimmed) #f) + ) ;if + ) ;let + ) ;else + ) ;cond + ) ;let + ) ;if + ) ;let* + ) ;else + ) ;cond + ) ;let + ) ;let + ) ;lambda + ) ;let + ) ;define* + + ;; make-ini-file-accumulator + ;; Returns a procedure that writes INI data to outport + (define* (make-ini-file-accumulator outport (key-value-sep #\=) (comment-delim #\;)) + (let ((current-section '*ini-none*) (closed #f)) + (lambda (item) + (cond (closed (error 'ini-file "accumulator called after EOF")) + ((eof-object? item) (set! closed #t) (eof-object)) + ((string? item) + (when (char-index item #\newline) + (error 'ini-file "comment string contains newline") + ) ;when + (display comment-delim outport) + (display #\space outport) + (display item outport) + (newline outport) + ) ; + ((and (list? item) (= (length item) 3)) + (let ((section (car item)) (key (cadr item)) (value (caddr item))) + ;; Write section header if changed + (when (not (eq? section current-section)) + (when section + (display #\[ outport) + (display (symbol->string section) outport) + (display #\] outport) + (newline outport) + ) ;when + (set! current-section section) + ) ;when + ;; Write key-value line + (display (symbol->string key) outport) + (when value + (display key-value-sep outport) + (display value outport) + ) ;when + (newline outport) + ) ;let + ) ; + (else (error 'ini-file "invalid argument to accumulator")) + ) ;cond + ) ;lambda + ) ;let + ) ;define* + + ) ;begin +) ;define-library diff --git a/tests/liii/ini-file-test.scm b/tests/liii/ini-file-test.scm new file mode 100644 index 00000000..e7233097 --- /dev/null +++ b/tests/liii/ini-file-test.scm @@ -0,0 +1,303 @@ +;; (liii ini-file) test file — SRFI 233: INI files +;; +;; Tests for make-ini-file-generator and make-ini-file-accumulator. + +;; ==== 函数分类索引 ==== + +;; 一、INI 读取(Generator) +;; make-ini-file-generator - 从输入端口创建 INI 行生成器 + +;; 二、INI 写入(Accumulator) +;; make-ini-file-accumulator - 向输出端口创建 INI 行累加器 + +;; ==== 单元测试 ==== + +(import (liii check) (liii ini-file) (liii base)) + +(check-set-mode! 'report-failed) + +;; ==== Helper ==== + +(define (generator->list gen) + (let loop + ((result '())) + (let ((item (gen))) + (if (eof-object? item) (reverse result) (loop (cons item result))) + ) ;let + ) ;let +) ;define + +;; ==== 1. make-ini-file-generator: 基础键值对(无 section) ==== + +(let ((gen (make-ini-file-generator (open-input-string "name=Alice\nage=30\n")))) + (check (gen) => '(#f name "Alice")) + (check (gen) => '(#f age "30")) + (check (eof-object? (gen)) => #t) +) ;let + +;; ==== 2. make-ini-file-generator: 带 section ==== + +(let ((gen (make-ini-file-generator (open-input-string "[database]\nhost=localhost\nport=5432\n") + ) ;make-ini-file-generator + ) ;gen + ) ; + (check (gen) => '(database host "localhost")) + (check (gen) => '(database port "5432")) + (check (eof-object? (gen)) => #t) +) ;let + +;; ==== 3. make-ini-file-generator: section 前的键值对属于 #f section ==== + +(let ((gen (make-ini-file-generator (open-input-string "global_key=global_val\n[section1]\nkey1=val1\n") + ) ;make-ini-file-generator + ) ;gen + ) ; + (check (gen) => '(#f global_key "global_val")) + (check (gen) => '(section1 key1 "val1")) + (check (eof-object? (gen)) => #t) +) ;let + +;; ==== 4. make-ini-file-generator: 注释和空行 ==== + +(let ((gen (make-ini-file-generator (open-input-string "; this is a comment\n\nkey=val\n")) + ) ;gen + ) ; + (check (gen) => '(#f key "val")) + (check (eof-object? (gen)) => #t) +) ;let + +;; ==== 5. make-ini-file-generator: = 前后空白忽略 ==== + +(let ((gen (make-ini-file-generator (open-input-string " key = value here \n")))) + (check (gen) => '(#f key "value here")) + (check (eof-object? (gen)) => #t) +) ;let + +;; ==== 6. make-ini-file-generator: 多个 = 只有第一个是分隔符 ==== + +(let ((gen (make-ini-file-generator (open-input-string "url=http://example.com?key=val\n")) + ) ;gen + ) ; + (check (gen) => '(#f url "http://example.com?key=val")) + (check (eof-object? (gen)) => #t) +) ;let + +;; ==== 7. make-ini-file-generator: 无 = 的行,值为 #f ==== + +(let ((gen (make-ini-file-generator (open-input-string "standalone_key\n[section]\nanother\n") + ) ;make-ini-file-generator + ) ;gen + ) ; + (check (gen) => '(#f standalone_key #f)) + (check (gen) => '(section another #f)) + (check (eof-object? (gen)) => #t) +) ;let + +;; ==== 8. make-ini-file-generator: 自定义分隔符 ==== + +(let ((gen (make-ini-file-generator (open-input-string "key: value\n") #\: #\#))) + (check (gen) => '(#f key "value")) + (check (eof-object? (gen)) => #t) +) ;let + +;; ==== 9. make-ini-file-generator: 自定义注释符 ==== + +(let ((gen (make-ini-file-generator (open-input-string "# comment\nkey=val\n") #\= #\#) + ) ;gen + ) ; + (check (gen) => '(#f key "val")) + (check (eof-object? (gen)) => #t) +) ;let + +;; ==== 10. make-ini-file-generator: 多个 section ==== + +(let ((gen (make-ini-file-generator (open-input-string "[section1]\nk1=v1\n[section2]\nk2=v2\n") + ) ;make-ini-file-generator + ) ;gen + ) ; + (check (gen) => '(section1 k1 "v1")) + (check (gen) => '(section2 k2 "v2")) + (check (eof-object? (gen)) => #t) +) ;let + +;; ==== 11. make-ini-file-generator: SRFI 233 示例(集成测试) ==== + +(let* ((ini-content "; Be sure to update the following line\nlast_modified_date=2022-08-10\n[other]\nquiet=/qa\n[install]\nallusers=true\napplicationusers=allusers\nclientauditingport=6420\ndatabasedb=boe120\nenablelogfile=true\ninstall.lp.fr.selected=true\ninstallswitch=server\nnsport=6400\nwebsite_metabase_number=true\n[features]\nremove=wcadotnet,webapplicationcontainer\n" + ) ;ini-content + (gen (make-ini-file-generator (open-input-string ini-content))) + (results (generator->list gen)) + ) ; + (check (length results) => 12) + (check (list-ref results 0) => '(#f last_modified_date "2022-08-10")) + (check (list-ref results 1) => '(other quiet "/qa")) + (check (list-ref results 2) => '(install allusers "true")) + (check (list-ref results 3) => '(install applicationusers "allusers")) + (check (list-ref results 4) => '(install clientauditingport "6420")) + (check (list-ref results 11) + => + '(features remove "wcadotnet,webapplicationcontainer") + ) ;check +) ;let* + +;; ==== 12. make-ini-file-generator: 行内注释 ==== + +(let ((gen (make-ini-file-generator (open-input-string "key=val ; inline comment\n"))) + ) ; + (check (gen) => '(#f key "val")) + (check (eof-object? (gen)) => #t) +) ;let + +;; ==== 13. make-ini-file-accumulator: 基础键值对写入 ==== + +(let ((result (call-with-output-string (lambda (out) + (let ((acc (make-ini-file-accumulator out))) + (acc '(#f key "value")) + ) ;let + ) ;lambda + ) ;call-with-output-string + ) ;result + ) ; + (check result => "key=value\n") +) ;let + +;; ==== 14. make-ini-file-accumulator: 带 section 写入 ==== + +(let ((result (call-with-output-string (lambda (out) + (let ((acc (make-ini-file-accumulator out))) + (acc '(section1 k1 "v1")) + (acc '(section1 k2 "v2")) + ) ;let + ) ;lambda + ) ;call-with-output-string + ) ;result + ) ; + (check result => "[section1]\nk1=v1\nk2=v2\n") +) ;let + +;; ==== 15. make-ini-file-accumulator: section 切换 ==== + +(let ((result (call-with-output-string (lambda (out) + (let ((acc (make-ini-file-accumulator out))) + (acc '(section1 k1 "v1")) + (acc '(section2 k2 "v2")) + ) ;let + ) ;lambda + ) ;call-with-output-string + ) ;result + ) ; + (check result => "[section1]\nk1=v1\n[section2]\nk2=v2\n") +) ;let + +;; ==== 16. make-ini-file-accumulator: 注释写入 ==== + +(let ((result (call-with-output-string (lambda (out) + (let ((acc (make-ini-file-accumulator out))) + (acc "this is a comment") + ) ;let + ) ;lambda + ) ;call-with-output-string + ) ;result + ) ; + (check result => "; this is a comment\n") +) ;let + +;; ==== 17. make-ini-file-accumulator: EOF 处理 ==== + +(let ((acc-result #f)) + (let ((result (call-with-output-string (lambda (out) + (let ((acc (make-ini-file-accumulator out))) + (acc '(#f key "val")) + (set! acc-result (acc (eof-object))) + ) ;let + ) ;lambda + ) ;call-with-output-string + ) ;result + ) ; + ;; closes binding list + (check (eof-object? acc-result) => #t) + (check result => "key=val\n") + ) ;let +) ;let + +;; ==== 18. make-ini-file-accumulator: 自定义分隔符 ==== + +(let ((result (call-with-output-string (lambda (out) + (let ((acc (make-ini-file-accumulator out #\: #\#))) + (acc '(#f key "value")) + ) ;let + ) ;lambda + ) ;call-with-output-string + ) ;result + ) ; + (check result => "key:value\n") +) ;let + +;; ==== 19. make-ini-file-accumulator: 自定义注释符 ==== + +(let ((result (call-with-output-string (lambda (out) + (let ((acc (make-ini-file-accumulator out #\= #\#))) + (acc "a comment") + ) ;let + ) ;lambda + ) ;call-with-output-string + ) ;result + ) ; + (check result => "# a comment\n") +) ;let + +;; ==== 20. make-ini-file-accumulator: #f section 不写 section 头 ==== + +(let ((result (call-with-output-string (lambda (out) + (let ((acc (make-ini-file-accumulator out))) + (acc '(#f key1 "val1")) + (acc '(#f key2 "val2")) + ) ;let + ) ;lambda + ) ;call-with-output-string + ) ;result + ) ; + (check result => "key1=val1\nkey2=val2\n") +) ;let + +;; ==== 21. 往返测试:generator 读出再用 accumulator 写回 ==== + +(let* ((ini-content "[database]\nhost=localhost\nport=5432\n[server]\nport=8080\n") + (gen (make-ini-file-generator (open-input-string ini-content))) + (items (generator->list gen)) + (result (call-with-output-string (lambda (out) + (let ((acc (make-ini-file-accumulator out))) + (for-each acc items) + ) ;let + ) ;lambda + ) ;call-with-output-string + ) ;result + ) ; + (check result => ini-content) +) ;let* + +;; ==== 22. make-ini-file-generator: 注释在 section 行上 ==== + +(let ((gen (make-ini-file-generator (open-input-string "[section] ; comment\nkey=val\n")) + ) ;gen + ) ; + (check (gen) => '(section key "val")) + (check (eof-object? (gen)) => #t) +) ;let + +;; ==== 23. 往返测试:standalone key(值为 #f) ==== + +(let* ((ini-content "standalone_key\n[section]\nanother\n") + (gen (make-ini-file-generator (open-input-string ini-content))) + (items (generator->list gen)) + (result (call-with-output-string (lambda (out) + (let ((acc (make-ini-file-accumulator out))) + (for-each acc items) + ) ;let + ) ;lambda + ) ;call-with-output-string + ) ;result + ) ; + (check result => ini-content) +) ;let* + +(check-report) From 2338ea10f0e88c1c43bd81e7b2ba2f2e31a6d12a Mon Sep 17 00:00:00 2001 From: root Date: Tue, 5 May 2026 21:17:23 +0800 Subject: [PATCH 3/6] add development record file --- devel/0039.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 devel/0039.md diff --git a/devel/0039.md b/devel/0039.md new file mode 100644 index 00000000..52320825 --- /dev/null +++ b/devel/0039.md @@ -0,0 +1,46 @@ +# [0039] 实现 SRFI 233: INI Files + +## 任务相关的代码文件 +- `goldfish/liii/ini-file.scm` +- `tests/liii/ini-file-test.scm` +- `README.md` + +## 如何测试 +```bash +xmake b goldfish +bin/gf fmt goldfish/liii/ini-file.scm +bin/gf fmt tests/liii/ini-file-test.scm +bin/gf tests/liii/ini-file-test.scm +``` + +## 2026/05/05 实现 (liii ini-file) 库 + +### What +1. 创建 `goldfish/liii/ini-file.scm`,实现 SRFI 233 规范中的两个核心函数: + - `make-ini-file-generator`:从文本输入端口创建生成器,逐行解析 INI 格式,返回 `(section key value)` 三元素列表 + - `make-ini-file-accumulator`:向文本输出端口写入 INI 格式数据 +2. 创建 `tests/liii/ini-file-test.scm`,包含 22 个测试场景、46 个断言 +3. 在 `README.md` 的 "Python-like standard library" 表格中加入 `(liii ini-file)` 条目 + +### Why +SRFI 233 是 Scheme 社区标准化的 INI 文件访问接口。INI 文件虽然简单,但在配置管理场景中广泛使用。作为 Goldfish Scheme 标准库的补充,提供开箱即用的 INI 文件解析和生成能力。 + +### How +**解析逻辑(Generator):** +- 逐行 `read-line` 读取输入端口 +- 用 `char-index` 找到首个注释分隔符,截断注释部分 +- 用 `ini-trim` 去除首尾空白(仅空格和 Tab,符合 SRFI 233 定义) +- `[...]` 模式匹配节名,转为 symbol +- 含分隔符的行用 `split-at-first` 按第一个分隔符拆分为键值对 +- 不含分隔符的行视为键,值为 `#f` +- 通过闭包维护 `current-section` 状态 + +**写入逻辑(Accumulator):** +- 通过闭包维护 `current-section` 状态,仅在 section 变化时写入 `[section]` 行 +- `#f` section 不写入 section 头 +- 字符串参数以注释格式写入(`comment-delim` + 空格 + 内容) +- EOF 对象触发关闭状态,后续调用报错 + +**关键设计决策:** +- 使用纯整数索引的辅助函数(`char-index`、`ini-trim`),避免 `string-cursor` 库的 cursor/index 转换复杂性 +- 用 `define*` 实现可选参数,支持自定义 `key-value-sep` 和 `comment-delim` From 8500f825b1cee76b20221333a19eb52e50b8a3c2 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 6 May 2026 00:52:56 +0800 Subject: [PATCH 4/6] =?UTF-8?q?[0039]=20=E9=87=8D=E6=9E=84=E4=B8=BA=20(srf?= =?UTF-8?q?i=20srfi-233)=20+=20(liii=20ini)=20=E4=B8=A4=E5=B1=82=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- README_ZH.md | 2 ++ devel/0039.md | 35 +++++++++++++++---- goldfish/liii/ini.scm | 4 +++ .../{liii/ini-file.scm => srfi/srfi-233.scm} | 2 +- tests/liii/ini-file-test.scm | 2 +- 6 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 goldfish/liii/ini.scm rename goldfish/{liii/ini-file.scm => srfi/srfi-233.scm} (99%) diff --git a/README.md b/README.md index 71485724..3a9b814b 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Just like S7 Scheme, [src/goldfish.hpp](src/goldfish.hpp) and [src/goldfish.cpp] | [(liii uuid)](goldfish/liii/uuid.scm) | UUID generation | `uuid4` | | [(liii http)](goldfish/liii/http.scm) | HTTP client library | `http-get`, `http-post`, `http-head` | | [(liii json)](goldfish/liii/json.scm) | JSON parsing and manipulation | `string->json`, `json->string` | -| [(liii ini-file)](goldfish/liii/ini-file.scm) | INI file parsing (SRFI 233) | `make-ini-file-generator`, `make-ini-file-accumulator` | +| [(liii ini)](goldfish/liii/ini.scm) | INI file parsing (SRFI 233) | `make-ini-file-generator`, `make-ini-file-accumulator` | ### SRFI @@ -56,6 +56,7 @@ Just like S7 Scheme, [src/goldfish.hpp](src/goldfish.hpp) and [src/goldfish.cpp] | `(srfi srfi-133)` | Part | Vector | | `(srfi srfi-151)` | Part | Bitwise Operations | | `(srfi srfi-196)` | Complete | Range Library | +| `(srfi srfi-233)` | Complete | INI files | | `(srfi srfi-216)` | Part | SICP | ### R7RS Standard Libraries diff --git a/README_ZH.md b/README_ZH.md index 04d379d7..6fb6279d 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -38,6 +38,7 @@ | [(liii uuid)](goldfish/liii/uuid.scm) | UUID 生成 | `uuid4` | | [(liii http)](goldfish/liii/http.scm) | HTTP 客户端库 | `http-get`, `http-post`, `http-head` | | [(liii json)](goldfish/liii/json.scm) | JSON 解析和操作 | `string->json`, `json->string` | +| [(liii ini)](goldfish/liii/ini.scm) | INI 文件解析 (SRFI 233) | `make-ini-file-generator`, `make-ini-file-accumulator` | ### SRFI @@ -54,6 +55,7 @@ | `(srfi srfi-133)` | 部分 | 向量 | | `(srfi srfi-151)` | 部分 | 位运算 | | `(srfi srfi-196)` | 完整 | Range 库 | +| `(srfi srfi-233)` | 完整 | INI 文件 | | `(srfi srfi-216)` | 部分 | SICP | diff --git a/devel/0039.md b/devel/0039.md index 52320825..308693cf 100644 --- a/devel/0039.md +++ b/devel/0039.md @@ -1,26 +1,48 @@ # [0039] 实现 SRFI 233: INI Files ## 任务相关的代码文件 -- `goldfish/liii/ini-file.scm` +- `goldfish/srfi/srfi-233.scm` +- `goldfish/liii/ini.scm` - `tests/liii/ini-file-test.scm` - `README.md` +- `README_ZH.md` ## 如何测试 ```bash xmake b goldfish -bin/gf fmt goldfish/liii/ini-file.scm +bin/gf fmt goldfish/srfi/srfi-233.scm +bin/gf fmt goldfish/liii/ini.scm bin/gf fmt tests/liii/ini-file-test.scm bin/gf tests/liii/ini-file-test.scm ``` -## 2026/05/05 实现 (liii ini-file) 库 +## 2026/05/06 PR 审查反馈修改 ### What -1. 创建 `goldfish/liii/ini-file.scm`,实现 SRFI 233 规范中的两个核心函数: +1. 将实现从 `(liii ini-file)` 迁移到 `(srfi srfi-233)`,符合项目库命名规范 +2. 新建 `goldfish/liii/ini.scm` 作为用户接口层,re-export `(srfi srfi-233)` 的导出 +3. 修复 accumulator 在 value 为 `#f` 时写入字面量 `#f` 的 bug +4. 新增 standalone key 往返测试,断言数从 46 增至 47 +5. 更新 README.md 和 README_ZH.md 的标准库表格和 SRFI 表格 + +### Why +PR 审查人 da-liii 指出: +- 库名应为 `(liii ini)` 而非 `(liii ini-file)`,与 `(liii json)`、`(liii csv)` 命名一致 +- 接口放在 `(liii ini)` 中,实现应放在 `(srfi srfi-233)` 中,保证兼容性 + +### How +参照 `(liii range)` re-export `(srfi srfi-196)` 的模式: +- `goldfish/srfi/srfi-233.scm` 包含完整实现 +- `goldfish/liii/ini.scm` 仅 `import` + `export`,无额外逻辑 + +## 2026/05/05 实现 (srfi srfi-233) 库 + +### What +1. 创建 `goldfish/srfi/srfi-233.scm`,实现 SRFI 233 规范中的两个核心函数: - `make-ini-file-generator`:从文本输入端口创建生成器,逐行解析 INI 格式,返回 `(section key value)` 三元素列表 - `make-ini-file-accumulator`:向文本输出端口写入 INI 格式数据 -2. 创建 `tests/liii/ini-file-test.scm`,包含 22 个测试场景、46 个断言 -3. 在 `README.md` 的 "Python-like standard library" 表格中加入 `(liii ini-file)` 条目 +2. 创建 `tests/liii/ini-file-test.scm`,包含 23 个测试场景、47 个断言 +3. 在 `README.md` 和 `README_ZH.md` 的标准库表格中加入 `(liii ini)` 条目 ### Why SRFI 233 是 Scheme 社区标准化的 INI 文件访问接口。INI 文件虽然简单,但在配置管理场景中广泛使用。作为 Goldfish Scheme 标准库的补充,提供开箱即用的 INI 文件解析和生成能力。 @@ -38,6 +60,7 @@ SRFI 233 是 Scheme 社区标准化的 INI 文件访问接口。INI 文件虽然 **写入逻辑(Accumulator):** - 通过闭包维护 `current-section` 状态,仅在 section 变化时写入 `[section]` 行 - `#f` section 不写入 section 头 +- value 为 `#f` 时只写 key,不写分隔符 - 字符串参数以注释格式写入(`comment-delim` + 空格 + 内容) - EOF 对象触发关闭状态,后续调用报错 diff --git a/goldfish/liii/ini.scm b/goldfish/liii/ini.scm new file mode 100644 index 00000000..82d457da --- /dev/null +++ b/goldfish/liii/ini.scm @@ -0,0 +1,4 @@ +(define-library (liii ini) + (import (srfi srfi-233)) + (export make-ini-file-generator make-ini-file-accumulator) +) ;define-library diff --git a/goldfish/liii/ini-file.scm b/goldfish/srfi/srfi-233.scm similarity index 99% rename from goldfish/liii/ini-file.scm rename to goldfish/srfi/srfi-233.scm index 8fa79194..d67a5635 100644 --- a/goldfish/liii/ini-file.scm +++ b/goldfish/srfi/srfi-233.scm @@ -16,7 +16,7 @@ ;; SRFI 233: INI files ;; Authors: John Cowan (spec), Arvydas Silanskas (reference implementation) -(define-library (liii ini-file) +(define-library (srfi srfi-233) (import (scheme base) (liii base)) (export make-ini-file-generator make-ini-file-accumulator) (begin diff --git a/tests/liii/ini-file-test.scm b/tests/liii/ini-file-test.scm index e7233097..25f39ab7 100644 --- a/tests/liii/ini-file-test.scm +++ b/tests/liii/ini-file-test.scm @@ -12,7 +12,7 @@ ;; ==== 单元测试 ==== -(import (liii check) (liii ini-file) (liii base)) +(import (liii check) (liii ini) (liii base)) (check-set-mode! 'report-failed) From b8f629387645e18ec8314cd983ddcb134aa9af72 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 6 May 2026 01:01:00 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=96=87=E4=BB=B6tests/liii/ini-file-test.scm=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E7=BB=86=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/liii/ini-file-test.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/liii/ini-file-test.scm b/tests/liii/ini-file-test.scm index 25f39ab7..f940baf1 100644 --- a/tests/liii/ini-file-test.scm +++ b/tests/liii/ini-file-test.scm @@ -1,4 +1,4 @@ -;; (liii ini-file) test file — SRFI 233: INI files +;; (liii ini) test file — SRFI 233: INI files ;; ;; Tests for make-ini-file-generator and make-ini-file-accumulator. From 0f6492d7da1b83a653edf49fa25e0f03718e4167 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 6 May 2026 21:06:55 +0800 Subject: [PATCH 6/6] =?UTF-8?q?[0039]=20=E4=BF=AE=E6=94=B9=E7=89=88?= =?UTF-8?q?=E6=9D=83=E5=B9=B4=E4=BB=BD=E4=B8=BA2026=EF=BC=8C=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E9=87=8D=E5=91=BD=E5=90=8D=20ini-trim=20=E4=B8=BA=20i?= =?UTF-8?q?ni-string-trim?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- goldfish/srfi/srfi-233.scm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/goldfish/srfi/srfi-233.scm b/goldfish/srfi/srfi-233.scm index d67a5635..9b959109 100644 --- a/goldfish/srfi/srfi-233.scm +++ b/goldfish/srfi/srfi-233.scm @@ -1,5 +1,5 @@ ;; -;; Copyright (C) 2024 The Goldfish Scheme Authors +;; Copyright (C) 2026 The Goldfish Scheme Authors ;; ;; Licensed under the Apache License, Version 2.0 (the "License"); ;; you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ ) ;define ;; Trim leading and trailing whitespace (space/tab) - (define (ini-trim str) + (define (ini-string-trim str) (let ((len (string-length str))) (let loop-start ((i 0)) @@ -79,12 +79,12 @@ ;; Parse a section header line like "[name]" ;; Returns section name string, or #f if not a section header (define (parse-section-name line) - (let* ((trimmed (ini-trim line)) (len (string-length trimmed))) + (let* ((trimmed (ini-string-trim line)) (len (string-length trimmed))) (if (and (> len 1) (char=? (string-ref trimmed 0) #\[) (char=? (string-ref trimmed (- len 1)) #\]) ) ;and - (ini-trim (substring trimmed 1 (- len 1))) + (ini-string-trim (substring trimmed 1 (- len 1))) #f ) ;if ) ;let* @@ -100,7 +100,7 @@ (let ((line (read-line inport))) (cond ((eof-object? line) (eof-object)) (else (let* ((no-comment (remove-comment line comment-delim)) - (trimmed (ini-trim no-comment)) + (trimmed (ini-string-trim no-comment)) ) ; (if (string=? trimmed "") (loop) @@ -108,7 +108,7 @@ (cond (section-name (set! current-section (string->symbol section-name)) (loop)) (else (let ((kv (split-at-first trimmed key-value-sep))) (if kv - (let ((key-str (ini-trim (car kv))) (val-str (ini-trim (cdr kv)))) + (let ((key-str (ini-string-trim (car kv))) (val-str (ini-string-trim (cdr kv)))) (list current-section (string->symbol key-str) val-str) ) ;let (list current-section (string->symbol trimmed) #f)