-
Notifications
You must be signed in to change notification settings - Fork 1
/
scheme-string.lisp
181 lines (162 loc) · 6.04 KB
/
scheme-string.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
;;;; -*- mode: common-lisp; -*-
;;;; Note: SBCL requires the use of SB-UNICODE for full Unicode
;;;; support for these various functions, but other Unicode-supporting
;;;; implementations just directly support full Unicode in the CL
;;;; package for things like upcasing.
(in-package #:airship-scheme)
(define-function (digit-value :inline t) (char)
"
Convert a digit character into the number it represents. For other
characters, return NIL.
"
#+sbcl
(sb-unicode:numeric-value char)
#-sbcl
(digit-char-p char))
(define-function (char-numeric-p :inline t) (char)
"Test to see if a character is numeric."
(and (digit-value char) t))
(define-function (char-alphabetic-p :inline t) (char)
"Test to see if a character is alphabetic."
#+sbcl
(sb-unicode:alphabetic-p char)
#-sbcl
(alpha-char-p char))
;;; Note: This might not be correct for all implementations, since as
;;; noted before, a CL implementation can support full Unicode without
;;; requiring calls to a Unicode library. Unfortunately, CL has no
;;; whitespace test built in.
(define-function (char-whitespace-p :inline t) (char)
"Test to see if a character represents whitespace."
#+sbcl
(and (sb-unicode:whitespace-p char) t)
#-sbcl
(or (char= char #\Newline)
(char= char #\Space)
(char= char #\Tab)))
(define-function (char-upper-case-p :inline t) (letter)
"Test to see if a character is upper case."
#+sbcl
(sb-unicode:uppercase-p letter)
#-sbcl
(upper-case-p letter))
(define-function (char-lower-case-p :inline t) (letter)
"Test to see if a character is lower case."
#+sbcl
(sb-unicode:lowercase-p letter)
#-sbcl
(lower-case-p letter))
(define-function (char-upcase* :inline t) ((char character))
"Upcase a character by Unicode rules."
#+sbcl
(let ((s (make-string 1 :initial-element char)))
(declare (dynamic-extent s))
(char (sb-unicode:uppercase s) 0))
#-sbcl
(char-upcase char))
(define-function (char-downcase* :inline t) ((char character))
"Downcase a character by Unicode rules."
#+sbcl
(let ((s (make-string 1 :initial-element char)))
(declare (dynamic-extent s))
(char (sb-unicode:lowercase s) 0))
#-sbcl
(char-downcase char))
;;; Note: This is another function which might not be correct for all
;;; implementations.
(define-function (char-foldcase :inline t) ((char character))
"Foldcase a character by Unicode rules."
#+sbcl
(let ((s (make-string 1 :initial-element char)))
(declare (dynamic-extent s))
(char (sb-unicode:casefold s) 0))
#-sbcl
(char-downcase char))
(define-function (string-upcase* :inline t) (string)
"Upcase a string by Unicode rules."
#+sbcl
(sb-unicode:uppercase string)
#-sbcl
(string-upcase string))
(define-function (string-downcase* :inline t) (string)
"Downcase a string by Unicode rules."
#+sbcl
(sb-unicode:lowercase string)
#-sbcl
(string-downcase string))
;;; Note: This might not be correct for all implementations.
(define-function (string-foldcase :inline t) (string)
"Foldcase a string by Unicode rules."
#+sbcl
(sb-unicode:casefold string)
#-sbcl
(string-downcase string))
(eval-when (:compile-toplevel :load-toplevel :execute)
(define-function (compare :inline t) ((function function) (items list))
"
Defines a short-circuiting predicate on an arbitrary-length list.
"
(when (endp items)
(error "Expected at least one item"))
(loop :for old-item := nil :then item
:for item :in items
:for match := t :then (funcall function old-item item)
:always match))
#+sbcl
(define-function (compare-foldcase :inline t) ((function function) (strings list))
"
Defines a short-circuiting string predicate on an arbitrary-length
list of strings, while doing a Unicode foldcase on each string.
"
(when (endp strings)
(error "Expected at least one item"))
(loop :for old-string := nil :then string*
:for string :in strings
:for string* := (string-foldcase string)
:for match := t :then (funcall function old-string string*)
:always match)))
(defmacro define-string-predicate ((binary-name n-ary-name) binary-predicate &key foldcase)
(let ((compare (if foldcase 'compare-foldcase 'compare)))
`(progn
(define-function (,binary-name :inline t) (string-1 string-2)
(,binary-predicate string-1 string-2))
(define-compiler-macro ,n-ary-name (&whole whole &rest strings)
(if strings
(if (endp (cdr strings))
;; TODO: optimize the one-arg version properly
whole
(if (endp (cddr strings))
(list ',binary-name (car strings) (cadr strings))
whole))
whole))
(define-function ,n-ary-name (&rest strings)
(,compare (function ,binary-name) strings)))))
(defmacro define-string-predicates (&body predicates)
`(progn
,@(mapcar (lambda (definition)
`(define-string-predicate ,@definition))
predicates)))
#+sbcl
(define-string-predicates
((%string=? string=?) sb-unicode:unicode=)
((%string-ci=? string-ci=?) sb-unicode:unicode-equal)
((%string<? string<?) sb-unicode:unicode<)
((%string-ci<? string-ci<?) sb-unicode:unicode< :foldcase t)
((%string>? string>?) sb-unicode:unicode>)
((%string-ci>? string-ci>?) sb-unicode:unicode> :foldcase t)
((%string<=? string<=?) sb-unicode:unicode<=)
((%string-ci<=? string-ci<=?) sb-unicode:unicode<= :foldcase t)
((%string>=? string>=?) sb-unicode:unicode>=)
((%string-ci>=? string-ci>=?) sb-unicode:unicode>= :foldcase t))
#-sbcl
(define-string-predicates
((%string=? string=?) string=)
((%string-ci=? string-ci=?) string-equal)
((%string<? string<?) string<)
((%string-ci<? string-ci<?) string-lessp)
((%string>? string>?) string>)
((%string-ci>? string-ci>?) string-greaterp)
((%string<=? string<=?) string<=)
((%string-ci<=? string-ci<=?) string-not-greaterp)
((%string>=? string>=?) string>=)
((%string-ci>=? string-ci>=?) string-not-lessp))