summaryrefslogtreecommitdiff
path: root/src/tg/macros.lisp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tg/macros.lisp')
-rw-r--r--src/tg/macros.lisp118
1 files changed, 0 insertions, 118 deletions
diff --git a/src/tg/macros.lisp b/src/tg/macros.lisp
deleted file mode 100644
index 9577d94..0000000
--- a/src/tg/macros.lisp
+++ /dev/null
@@ -1,118 +0,0 @@
1;; SPDX-License-Identifier: EUPL-1.2
2;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
3(defpackage :ukkoclot/tg/macros
4 (:use :c2cl)
5 (:import-from :ukkoclot/bot/impl :bot :do-call :parse-value)
6 (:import-from :ukkoclot/hash-tables :gethash-lazy)
7 (:import-from :ukkoclot/strings :lisp->snake-case)
8 (:local-nicknames
9 (:jzon :com.inuoe.jzon))
10 (:export :define-tg-type))
11(in-package :ukkoclot/tg/macros)
12
13(eval-when (:compile-toplevel :load-toplevel :execute)
14 (defstruct (field (:constructor make-field%)) name type default skip-if-default)
15
16 (defparameter +unique+ (gensym))
17
18 (defun make-field (name type &optional (default +unique+) &key (skip-if-default (not (eq default +unique+))))
19 (let ((default (if (eq default +unique+)
20 (list 'error (format nil "No value given for ~A" name))
21 default)))
22 (make-field% :name name
23 :type type
24 :default default
25 :skip-if-default skip-if-default)))
26
27 (defun parse-field-specs (field-specs)
28 (loop for field-spec in field-specs
29 collect (apply #'make-field field-spec)))
30
31 (defun field-hash-key (field)
32 (string-downcase (lisp->snake-case (symbol-name (field-name field)))))
33
34 (defun field-accessor (struc-name field)
35 (intern (concatenate 'string (symbol-name struc-name) "-" (symbol-name (field-name field)))))
36
37 (defun field->coerced-field-spec (field struc-name obj-name)
38 `(list ,(string-downcase (lisp->snake-case (symbol-name (field-name field))))
39 (,(field-accessor struc-name field) ,obj-name)
40 ',(field-type field)))
41
42 (defun field->defun-spec (field)
43 (list (field-name field) (field-default field)))
44
45 (defun field->format-arg (field name struc)
46 `(',(field-name field) (,(field-accessor name field) ,struc)))
47
48 (defun field->ftype-spec (field)
49 (list (intern (symbol-name (field-name field)) :keyword) (field-type field)))
50
51 (defun field->gethash-spec (field hash-table-sym)
52 (let ((hash-key (field-hash-key field)))
53 (list 'gethash-lazy hash-key hash-table-sym (field-default field))))
54
55 (defun field->sethash-spec (field name struc hash-table-sym)
56 (let ((hash-key (field-hash-key field))
57 (skip-if-default (field-skip-if-default field))
58 (default (field-default field)))
59 (if skip-if-default
60 (let ((tmpsym (gensym "TMP")))
61 `(let ((,tmpsym (,(field-accessor name field) ,struc)))
62 (unless (equal ,tmpsym ,default)
63 (setf (gethash ,hash-key ,hash-table-sym) ,tmpsym))))
64 `(setf (gethash ,hash-key ,hash-table-sym) (,(field-accessor name field) ,struc)))))
65
66 (defun field->let-gethash-spec (field hash-table-sym)
67 `(,(field-name field)
68 (parse-value ',(field-type field) ,(field->gethash-spec field hash-table-sym))))
69
70 (defun field->make-spec (field)
71 (list (intern (symbol-name (field-name field)) :keyword)
72 (field-name field)))
73
74 (defun field->struct-spec (field)
75 (list (field-name field) (field-default field) :type (field-type field))))
76
77(defmacro define-tg-type (name &body field-specs)
78 (let* ((fields (parse-field-specs field-specs))
79 (revfields (reverse fields))
80 (make-name (intern (concatenate 'string "MAKE-" (symbol-name name))))
81 (printer (gensym (concatenate 'string "PRINT-" (symbol-name name))))
82 (hash (gensym "HASH-"))
83 (struc (gensym (symbol-name name)))
84 (stream (gensym "STREAM"))
85 (depth (gensym "DEPTH"))
86 (pprint-args (gensym "PPRINT-ARGS"))
87 (res (gensym "RES"))
88 (type (gensym "TYPE")))
89 `(progn
90 (defstruct (,name (:print-function ,printer))
91 ,@(loop for field in fields
92 collect (field->struct-spec field)))
93 (defun ,printer (,struc ,stream ,depth)
94 (declare (ignore ,depth))
95 (let (,pprint-args)
96 ,@(loop for field in revfields
97 collecting
98 (if (field-skip-if-default field)
99 `(let ((value (,(field-accessor name field) ,struc)))
100 (unless (equal value ,(field-default field))
101 (setf ,pprint-args (list* ',(field-name field) value ,pprint-args))))
102 `(setf ,pprint-args (list* ',(field-name field) (,(field-accessor name field) ,struc) ,pprint-args))))
103 (format ,stream "~A~<[~;~@{~_~1I~W = ~W~^, ~}~;]~:>" ',name ,pprint-args)))
104 (defmethod parse-value ((,type (eql ',name)) ,hash)
105 (let ,(loop for field in fields
106 collect (field->let-gethash-spec field hash))
107 (,make-name ,@(loop for field in fields
108 append (field->make-spec field)))))
109 (defmethod jzon:coerced-fields ((,struc ,name))
110 (let (,res)
111 ,@(loop for field in revfields
112 collecting
113 (if (field-skip-if-default field)
114 `(let ((value (,(field-accessor name field) ,struc)))
115 (unless (equal value ,(field-default field))
116 (setf ,res (cons ,(field->coerced-field-spec field name struc) ,res))))
117 `(setf ,res (cons ,(field->coerced-field-spec field name struc) ,res))))
118 ,res)))))