From 8dd9849464cc5ef3725ae9dda6cff90d63b47e8f Mon Sep 17 00:00:00 2001 From: Da Shen Date: Sun, 2 Nov 2025 14:25:54 +0800 Subject: [PATCH 1/4] wip --- tests/goldfish/liii/lang-test.scm | 162 ------------------------------ tests/goldfish/liii/oop-test.scm | 162 ++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 162 deletions(-) diff --git a/tests/goldfish/liii/lang-test.scm b/tests/goldfish/liii/lang-test.scm index 98aa6635..8e2d00c0 100644 --- a/tests/goldfish/liii/lang-test.scm +++ b/tests/goldfish/liii/lang-test.scm @@ -25,168 +25,6 @@ (define == class=?) (check-set-mode! 'report-failed) -(check-catch 'syntax-error - (eval - '(define-case-class instance-methods-conflict-test - ((name string?) - (age integer?)) - (define (%name) - name)))) - -(check-catch 'syntax-error - (eval - '(define-case-class static-methods-conflict-test - ((name string?) - (age integer?)) - (define (@name) - name)))) - -(check-catch 'syntax-error - (eval - '(define-case-class internal-methods-conflict-test - ((name string?) - (test-name string?) - (age integer?)) - (define (test-name str) - (string-append str " "))))) - -(define-case-class person - ((name string? "Bob") - (age integer?))) - -(let1 bob (person :name "Bob" :age 21) - (check (bob 'name) => "Bob") - (check (bob 'age) => 21) - (check ((bob :name "hello") 'name) => "hello") - (check-catch 'value-error (bob 'sex)) - (check-catch 'value-error (bob :sex)) - (check-true (bob :is-instance-of 'person)) - (check-true (person :is-type-of bob)) - (check (bob :to-string) => "(person :name \"Bob\" :age 21)")) - -(check-catch 'type-error (person 1 21)) - -(let ((bob (person "Bob" 21)) - (get-name (lambda (x) - (case* x - ((#) (x 'name)) - (else (value-error)))))) - (check (get-name bob) => "Bob") - (check-catch 'value-error (get-name 1))) - -(define-case-class jerson - ((name string?) - (age integer?)) - - (define (%to-string) - (string-append "I am " name " " (number->string age) " years old!")) - (define (%greet x) - (string-append "Hi " x ", " (%to-string))) - ) - -(let1 bob (jerson "Bob" 21) - (check (bob :to-string) => "I am Bob 21 years old!") - (check (bob :greet "Alice") => "Hi Alice, I am Bob 21 years old!")) - -(define-case-class anonymous () - (define name "") - - (define (%get-name) name) - - (define (%set-name! x) - (set! name x)) - ) - -(let1 p (anonymous) - (p :set-name! "Alice") - (check (p :get-name) => "Alice")) - -(define-case-class my-bool () - (define data #t) - - (define (%set-true!) - (set! data #t)) - (define (%set-false!) - (set! data #f)) - - (define (%true?) data) - (define (%false?) (not (%true?))) - - (define (@apply x) - (let1 r (my-bool) - (cond ((eq? x 'true) - (r :set-true!)) - ((eq? x 'false) - (r :set-false!)) - ((boolean? x) - (if x (r :set-true!) (r :set-false!))) - (else (r :set-false!))) - r)) - ) - -(check-true ((my-bool 'true) :true?)) -(check-true ((my-bool 'false) :false?)) -(check-true ((my-bool #t) :true?)) -(check-true ((my-bool #f) :false?)) -(check-true (my-bool :is-type-of (my-bool 'true))) - -(define-case-class test-case-class - ((name string?)) - - (define (@this-is-a-static-method) - (test-case-class "static")) - - (define (%this-is-a-instance-method) - (test-case-class (string-append name "instance"))) - ) - -(let1 hello (test-case-class "hello ") - (check-catch 'value-error (hello :this-is-a-static-method)) - (check (test-case-class :this-is-a-static-method) => (test-case-class "static"))) - -(let () - (define-case-class person ((name string?) (country string?)) - (define (@default) - (person "Andy" "China")) - (define (%set-country! c . xs) - (set! country c) - (apply (%this) (if (null? xs) '(:this) xs))) - (define (%set-name! n . xs) - (set! name n) - (apply (%this) (if (null? xs) '(:this) xs))) - (define (%to-string) - (format #f "Hello ~a from ~a" name country))) - - (define Andy (person :default)) - (check-catch 'wrong-type-arg (person :this)) - (check (Andy :to-string) => "Hello Andy from China") - (check (Andy :set-country! "USA" :to-string) => "Hello Andy from USA") - (check (Andy :to-string) => "Hello Andy from USA") - (check (Andy :set-country! "China" :set-name! "Ancker-0" :to-string) => "Hello Ancker-0 from China") - (check (Andy :set-country! "China") => (person "Ancker-0" "China")) - (check (Andy :this :set-country! "USA" :this :set-name! "Andy" :this :to-string) => "Hello Andy from USA") - (check-true (person :is-type-of Andy))) - -(let () - (define-case-class person ((name string?) (country string?)) - (chained-define (@default) - (person "Andy" "China")) - (chained-define (set-country! c) - (set! country c) - (%this)) - (chained-define (set-name! n) - (set! name n) - (%this)) - (chained-define (%set-both! n c) - (set-country! c) - (set-name! n) - (%this)) - (chained-define (%to-string) - (rich-string (format #f "Hello ~a from ~a" name country)))) - (check (person :default :to-string :get) => "Hello Andy from China") - (check (person :default :set-both! "Bob" "Russia" :to-string :get) => "Hello Bob from Russia") - (check-catch 'value-error (person :default :set-country! "French"))) - (define-object string-utils (define (@concat x y) (string-append x y)) diff --git a/tests/goldfish/liii/oop-test.scm b/tests/goldfish/liii/oop-test.scm index 13987e76..7c88f5d8 100644 --- a/tests/goldfish/liii/oop-test.scm +++ b/tests/goldfish/liii/oop-test.scm @@ -155,3 +155,165 @@ typed-define 是 (liii oop) 模块中用于定义类型安全函数的宏。它 (check (greet :message "Hi" :times 3) => "HiHiHi") (check-catch 'type-error (greet :times "not-a-number")) + +(check-catch 'syntax-error + (eval + '(define-case-class instance-methods-conflict-test + ((name string?) + (age integer?)) + (define (%name) + name)))) + +(check-catch 'syntax-error + (eval + '(define-case-class static-methods-conflict-test + ((name string?) + (age integer?)) + (define (@name) + name)))) + +(check-catch 'syntax-error + (eval + '(define-case-class internal-methods-conflict-test + ((name string?) + (test-name string?) + (age integer?)) + (define (test-name str) + (string-append str " "))))) + +(define-case-class person + ((name string? "Bob") + (age integer?))) + +(let1 bob (person :name "Bob" :age 21) + (check (bob 'name) => "Bob") + (check (bob 'age) => 21) + (check ((bob :name "hello") 'name) => "hello") + (check-catch 'value-error (bob 'sex)) + (check-catch 'value-error (bob :sex)) + (check-true (bob :is-instance-of 'person)) + (check-true (person :is-type-of bob)) + (check (bob :to-string) => "(person :name \"Bob\" :age 21)")) + +(check-catch 'type-error (person 1 21)) + +(let ((bob (person "Bob" 21)) + (get-name (lambda (x) + (case* x + ((#) (x 'name)) + (else (value-error)))))) + (check (get-name bob) => "Bob") + (check-catch 'value-error (get-name 1))) + +(define-case-class jerson + ((name string?) + (age integer?)) + + (define (%to-string) + (string-append "I am " name " " (number->string age) " years old!")) + (define (%greet x) + (string-append "Hi " x ", " (%to-string))) + ) + +(let1 bob (jerson "Bob" 21) + (check (bob :to-string) => "I am Bob 21 years old!") + (check (bob :greet "Alice") => "Hi Alice, I am Bob 21 years old!")) + +(define-case-class anonymous () + (define name "") + + (define (%get-name) name) + + (define (%set-name! x) + (set! name x)) + ) + +(let1 p (anonymous) + (p :set-name! "Alice") + (check (p :get-name) => "Alice")) + +(define-case-class my-bool () + (define data #t) + + (define (%set-true!) + (set! data #t)) + (define (%set-false!) + (set! data #f)) + + (define (%true?) data) + (define (%false?) (not (%true?))) + + (define (@apply x) + (let1 r (my-bool) + (cond ((eq? x 'true) + (r :set-true!)) + ((eq? x 'false) + (r :set-false!)) + ((boolean? x) + (if x (r :set-true!) (r :set-false!))) + (else (r :set-false!))) + r)) + ) + +(check-true ((my-bool 'true) :true?)) +(check-true ((my-bool 'false) :false?)) +(check-true ((my-bool #t) :true?)) +(check-true ((my-bool #f) :false?)) +(check-true (my-bool :is-type-of (my-bool 'true))) + +(define-case-class test-case-class + ((name string?)) + + (define (@this-is-a-static-method) + (test-case-class "static")) + + (define (%this-is-a-instance-method) + (test-case-class (string-append name "instance"))) + ) + +(let1 hello (test-case-class "hello ") + (check-catch 'value-error (hello :this-is-a-static-method)) + (check (test-case-class :this-is-a-static-method) => (test-case-class "static"))) + +(let () + (define-case-class person ((name string?) (country string?)) + (define (@default) + (person "Andy" "China")) + (define (%set-country! c . xs) + (set! country c) + (apply (%this) (if (null? xs) '(:this) xs))) + (define (%set-name! n . xs) + (set! name n) + (apply (%this) (if (null? xs) '(:this) xs))) + (define (%to-string) + (format #f "Hello ~a from ~a" name country))) + + (define Andy (person :default)) + (check-catch 'wrong-type-arg (person :this)) + (check (Andy :to-string) => "Hello Andy from China") + (check (Andy :set-country! "USA" :to-string) => "Hello Andy from USA") + (check (Andy :to-string) => "Hello Andy from USA") + (check (Andy :set-country! "China" :set-name! "Ancker-0" :to-string) => "Hello Ancker-0 from China") + (check (Andy :set-country! "China") => (person "Ancker-0" "China")) + (check (Andy :this :set-country! "USA" :this :set-name! "Andy" :this :to-string) => "Hello Andy from USA") + (check-true (person :is-type-of Andy))) + +(let () + (define-case-class person ((name string?) (country string?)) + (chained-define (@default) + (person "Andy" "China")) + (chained-define (set-country! c) + (set! country c) + (%this)) + (chained-define (set-name! n) + (set! name n) + (%this)) + (chained-define (%set-both! n c) + (set-country! c) + (set-name! n) + (%this)) + (chained-define (%to-string) + (rich-string (format #f "Hello ~a from ~a" name country)))) + (check (person :default :to-string :get) => "Hello Andy from China") + (check (person :default :set-both! "Bob" "Russia" :to-string :get) => "Hello Bob from Russia") + (check-catch 'value-error (person :default :set-country! "French"))) -- Gitee From e20d0b7d2086724c1b99bcd7bc9d614a4f436c68 Mon Sep 17 00:00:00 2001 From: Da Shen Date: Sun, 2 Nov 2025 14:28:47 +0800 Subject: [PATCH 2/4] wip --- devel/209_3.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 devel/209_3.md diff --git a/devel/209_3.md b/devel/209_3.md new file mode 100644 index 00000000..3dc6bb63 --- /dev/null +++ b/devel/209_3.md @@ -0,0 +1,29 @@ +# [209_3] 为 define-case-class 新增文档 + +## 任务相关的代码文件 +- goldfish/liii/oop.scm +- tests/goldfish/liii/oop-test.scm + +## 如何测试 +``` +xmake config --yes +xmake b goldfish +bin/goldfish tools/lint.scm goldfish/liii/oop.scm +bin/goldfish tools/lint.scm tests/goldfish/liii/oop-test.scm +bin/goldfish -m r7rs tests/goldfish/liii/oop-test.scm +``` + +## 2025/11/02 define-case-class 文档撰写 +### What +撰写 `(liii oop)` 模块中 `define-case-class` 宏的文档,包括语法、参数说明、使用示例和注意事项。 + +1. 分析 `define-case-class` 宏的实现逻辑和功能 +2. 编写详细的宏文档说明 +3. 提供丰富的使用示例 +4. 说明宏的行为特性和限制 + +### Why +`define-case-class` 是 `(liii oop)` 模块中的重要宏,用于定义类似Scala的case class。通过完善文档,帮助开发者更好地理解和使用该宏来创建类型安全的case class。 + +### How +通过分析源代码实现和现有测试用例,理解宏的参数处理、字段定义、方法定义和类型约束机制,编写全面准确的文档说明。 \ No newline at end of file -- Gitee From 94b71e0b5837b437035efe2ac633da5e23381ba0 Mon Sep 17 00:00:00 2001 From: Da Shen Date: Sun, 2 Nov 2025 14:37:31 +0800 Subject: [PATCH 3/4] =?UTF-8?q?[209=5F3]=20=E4=B8=BA=20define-case-class?= =?UTF-8?q?=20=E6=96=B0=E5=A2=9E=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- tests/goldfish/liii/oop-test.scm | 61 +++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/tests/goldfish/liii/oop-test.scm b/tests/goldfish/liii/oop-test.scm index 7c88f5d8..0b4400f6 100644 --- a/tests/goldfish/liii/oop-test.scm +++ b/tests/goldfish/liii/oop-test.scm @@ -14,7 +14,7 @@ ; under the License. ; -(import (liii oop) (liii check) (liii error)) +(import (liii oop) (liii check) (liii error) (liii base) (liii case) (liii rich-string)) (check-set-mode! 'report-failed) #| @@ -181,6 +181,65 @@ typed-define 是 (liii oop) 模块中用于定义类型安全函数的宏。它 (define (test-name str) (string-append str " "))))) +#| +define-case-class +定义类似 Scala 的 case class,提供类型安全的样本类。 + +语法 +---- +(define-case-class class-name fields . private-fields-and-methods) + +参数 +---- +class-name : symbol +要定义的 case class 名称。 + +fields : list +字段定义列表,每个字段格式为 (field-name type-predicate [default-value])。 + +private-fields-and-methods : any +可选的私有字段和方法定义。 + +返回值 +---- +procedure +返回一个函数,该函数可以用于创建 case class 实例或调用静态方法。 + +描述 +---- +`define-case-class` 是 (liii oop) 模块中用于定义样本类的核心宏。 +它创建类型安全的 case class,支持字段验证、方法分发和不可变数据结构。 + +字段定义中每个字段由三部分组成: +- field-name: 字段名称(符号) +- type-predicate: 类型断言函数,用于验证字段值的类型 +- default-value: 可选,字段的默认值 + +方法类型包括: +- 静态方法: 以 `@` 开头的函数定义,通过类名调用 +- 实例方法: 以 `%` 开头的函数定义,通过实例调用 +- 内部方法: 普通函数定义,仅在类内部可见 + +私有字段使用 `define` 定义,仅在类内部可见。 + +特点 +---- +- 类型安全: 创建实例时会自动验证字段类型 +- 不可变性: 字段默认不可变,通过关键字参数创建新实例 +- 模式匹配: 支持通过字段名访问字段值 +- 方法分发: 支持静态方法和实例方法 +- 相等性比较: 自动实现 `:equals` 方法 +- 字符串表示: 自动实现 `:to-string` 方法 + +注意事项 +---- +- 方法名不能与字段名冲突 +- 字段类型验证在运行时进行 +- 实例方法通过 `%` 前缀定义 +- 静态方法通过 `@` 前缀定义 +- 私有字段仅在类内部可见 +|# + (define-case-class person ((name string? "Bob") (age integer?))) -- Gitee From 736bb47bfbedebe0e6f12a9d8efacb68d3a9b14d Mon Sep 17 00:00:00 2001 From: Da Shen Date: Sun, 2 Nov 2025 14:39:33 +0800 Subject: [PATCH 4/4] =?UTF-8?q?[209=5F3]=20=E8=B0=83=E6=95=B4=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- tests/goldfish/liii/oop-test.scm | 52 +++++++++++++++++--------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/tests/goldfish/liii/oop-test.scm b/tests/goldfish/liii/oop-test.scm index 0b4400f6..c357e0db 100644 --- a/tests/goldfish/liii/oop-test.scm +++ b/tests/goldfish/liii/oop-test.scm @@ -156,31 +156,6 @@ typed-define 是 (liii oop) 模块中用于定义类型安全函数的宏。它 (check-catch 'type-error (greet :times "not-a-number")) -(check-catch 'syntax-error - (eval - '(define-case-class instance-methods-conflict-test - ((name string?) - (age integer?)) - (define (%name) - name)))) - -(check-catch 'syntax-error - (eval - '(define-case-class static-methods-conflict-test - ((name string?) - (age integer?)) - (define (@name) - name)))) - -(check-catch 'syntax-error - (eval - '(define-case-class internal-methods-conflict-test - ((name string?) - (test-name string?) - (age integer?)) - (define (test-name str) - (string-append str " "))))) - #| define-case-class 定义类似 Scala 的 case class,提供类型安全的样本类。 @@ -376,3 +351,30 @@ procedure (check (person :default :to-string :get) => "Hello Andy from China") (check (person :default :set-both! "Bob" "Russia" :to-string :get) => "Hello Bob from Russia") (check-catch 'value-error (person :default :set-country! "French"))) + +(check-catch 'syntax-error + (eval + '(define-case-class instance-methods-conflict-test + ((name string?) + (age integer?)) + (define (%name) + name)))) + +(check-catch 'syntax-error + (eval + '(define-case-class static-methods-conflict-test + ((name string?) + (age integer?)) + (define (@name) + name)))) + +(check-catch 'syntax-error + (eval + '(define-case-class internal-methods-conflict-test + ((name string?) + (test-name string?) + (age integer?)) + (define (test-name str) + (string-append str " "))))) + +(check-report) -- Gitee