forked from pixie-lang/pixie
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstring.pxi
More file actions
133 lines (118 loc) · 4.05 KB
/
string.pxi
File metadata and controls
133 lines (118 loc) · 4.05 KB
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
(ns pixie.string
(:require [pixie.stdlib :as std]
[pixie.string.internal :as si]))
; reexport native string functions
(def substring si/substring)
(def index-of (comp #(if (not= -1 %) %) si/index-of))
(def split si/split)
(defn split-lines
"Splits on \\n or \\r\\n, the two typical line breaks."
[s]
(when s (apply concat (map #(split % "\n") (split s "\r\n")))))
(def ends-with? si/ends-with)
(def starts-with? si/starts-with)
(def trim si/trim)
(def triml si/triml)
(def trimr si/trimr)
(def capitalize si/capitalize)
(def lower-case si/lower-case)
(def upper-case si/upper-case)
; TODO: There should be locale-aware variants of these values
(def lower "abcdefghijklmnopqrstuvwxyz")
(def upper "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
(def digits "0123456789")
(def punctuation "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")
(def whitespace (str \space \newline \tab \backspace \formfeed \return))
(def letters (str lower upper))
(def printable (str letters digits punctuation whitespace))
(def hexdigits "0123456789abcdefABCDEF")
(def octdigits "012345678")
(defn trim-newline
"Replace all trailing newline characters (\\r and \\n) from the end of a string."
[s]
(loop [index (count s)]
(if (zero? index)
""
(let [ch (nth s (dec index))]
(if (or (= ch \newline) (= ch \return))
(recur (dec index))
(substring s 0 index))))))
(defn replace
"Replace all occurrences of x in s with r."
[s x r]
(let [offset (if (zero? (count x)) (+ 1 (count r)) (count r))]
(loop [start 0
s s]
(if-let [i (index-of s x start)]
(recur (+ i offset) (str (substring s 0 i) r (substring s (+ i (count x)))))
s))))
(defn replace-first
"Replace the first occurrence of x in s with r."
[s x r]
(if-let [i (index-of s x)]
(str (substring s 0 i) r (substring s (+ i (count x))))
s))
(defn reverse
"Returns s with its characters reversed."
[s]
(when s
(apply str (std/reverse s))))
(defn join
{:doc "Join the elements of the collection using an optional separator"
:examples [["(require pixie.string :as s)"]
["(s/join [1 2 3])" nil "123"]
["(s/join \", \" [1 2 3])" nil "1, 2, 3"]]}
([coll] (join "" coll))
([separator coll]
(loop [s (seq coll)
res ""]
(cond
(nil? s) res
(nil? (next s)) (str res (first s))
:else (recur (next s) (str res (first s) separator))))))
(defn blank?
"True if s is nil, empty, or contains only whitespace."
[s]
(if s
(let [white (set whitespace)]
(every? white s))
true))
(defn escape
"Return a new string, using cmap to escape each character ch
from s as follows:
If (cmap ch) is nil, append ch to the new string.
If (cmap ch) is non-nil, append (str (cmap ch)) instead."
[s cmap]
(if (or (nil? s)
(nil? cmap))
s
(apply str (map #(if-let [c (cmap %)] c %)
(vec s)))))
(defmacro interp
; TODO: This might merit special read syntax
{:doc "String interpolation."
:examples [["(require pixie.string :refer [interp])"]
["(interp \"2 plus 2 is $(+ 2 2)$!\")" nil "2 plus 2 is 4!"]
["(let [x \"locals\"] (interp \"You can use arbitrary forms; for example $x$\"))"
nil "You can use arbitrary forms; for example locals"]
["(interp \"$$$$ is the escape for a literal $$\")"
nil "$$ is the escape for a literal $"]
]}
[txt]
(loop [forms [], txt txt]
(cond
(empty? txt) `(str ~@ forms)
(starts-with? txt "$")
(let [pos (or (index-of txt "$" 1)
(throw "Unmatched $ in interp argument!"))
form-str (subs txt 1 pos)
form (if (empty? form-str) "$"
(read-string form-str))
rest-str (subs txt (inc pos))]
(recur (conj forms form) rest-str))
:else
(let [pos (or (index-of txt "$")
(count txt))
form (subs txt 0 pos)
rest-str (subs txt pos)]
(recur (conj forms form) rest-str)))))