Skip to content
This repository was archived by the owner on Oct 7, 2024. It is now read-only.

Commit 1b919a8

Browse files
authored
Merge pull request #26 from xapix-io/missing-text-functions
Add ARABIC, VALUE and TEXTJOIN
2 parents 8256c5e + 5a5d452 commit 1b919a8

File tree

8 files changed

+279
-34
lines changed

8 files changed

+279
-34
lines changed

README.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ Any expression can be used as an operand for any operator. axel-f has the same o
107107
- [x] [AND](https://support.office.com/en-us/article/and-function-5f19b2e8-e1df-4408-897a-ce285a19e9d9)
108108
- [x] [OR](https://support.office.com/en-us/article/or-function-7d17ad14-8700-4281-b308-00b131e22af0)
109109
- [x] [NOT](https://support.office.com/en-us/article/not-function-9cfc6011-a054-40c7-a140-cd4ba2d87d77)
110+
- [x] [ARABIC](https://support.office.com/en-us/article/arabic-function-9a8da418-c17b-4ef9-a657-9370a30a674f)
110111
- [x] [CLEAN](https://support.office.com/en-us/article/clean-function-26f3d7c5-475f-4a9c-90e5-4b8ba987ba41)
111112
- [x] [CHAR](https://support.office.com/en-us/article/char-function-bbd249c8-b36e-4a91-8017-1c133f9b837a)
112113
- [x] [CODE](https://support.office.com/en-us/article/code-function-c32b692b-2ed0-4a04-bdd9-75640144b928)
@@ -120,15 +121,21 @@ Any expression can be used as an operand for any operator. axel-f has the same o
120121
- [x] [LOWER](https://support.office.com/en-us/article/lower-function-3f21df02-a80c-44b2-afaf-81358f9fdeb4)
121122
- [x] [MID](https://support.office.com/en-us/article/mid-midb-functions-d5f9e25c-d7d6-472e-b568-4ecb12433028)
122123
- [x] [PROPER](https://support.office.com/en-us/article/proper-function-52a5a283-e8b2-49be-8506-b2887b889f94)
124+
- [x] [REGEXEXTRACT](https://support.google.com/docs/answer/3098244?hl=en&ref_topic=3105625)
125+
- [x] [REGEXMATCH](https://support.google.com/docs/answer/3098292?hl=en&ref_topic=3105625)
126+
- [x] [REGEXREPLACE](https://support.google.com/docs/answer/3098245?hl=en&ref_topic=3105625)
123127
- [x] [REPLACE](https://support.office.com/en-us/article/replace-replaceb-functions-8d799074-2425-4a8a-84bc-82472868878a)
124128
- [x] [REPT](https://support.office.com/en-us/article/rept-function-04c4d778-e712-43b4-9c15-d656582bb061)
125129
- [x] [RIGHT](https://support.office.com/en-us/article/right-rightb-functions-240267ee-9afa-4639-a02b-f19e1786cf2f)
126130
- [x] [ROMAN](https://support.office.com/en-us/article/roman-function-d6b0b99e-de46-4704-a518-b45a0f8b56f5)
127131
- [x] [SEARCH](https://support.office.com/en-us/article/search-searchb-functions-9ab04538-0e55-4719-a72e-b6f54513b495)
128132
- [x] [SPLIT](https://support.google.com/docs/answer/3094136)
129133
- [x] [SUBSTITUTE](https://support.office.com/en-us/article/substitute-function-6434944e-a904-4336-a9b0-1e58df3bc332)
134+
- [x] [T](https://support.office.com/en-us/article/t-function-fb83aeec-45e7-4924-af95-53e073541228)
130135
- [x] [TRIM](https://support.office.com/en-us/article/trim-function-410388fa-c5df-49c6-b16c-9e5630b479f9)
131136
- [x] [UPPER](https://support.office.com/en-us/article/upper-function-c11f29b3-d1a3-4537-8df6-04d0049963d6)
137+
- [x] [VALUE](https://support.office.com/en-us/article/value-function-257d0108-07dc-437d-ae1c-bc2d3953d8c2)
138+
- [x] [TEXTJOIN](https://support.office.com/en-us/article/textjoin-function-357b449a-ec91-49d0-80c3-0e8fc845691c)
132139
- [x] [COUNT](https://support.office.com/en-us/article/count-function-a59cd7fc-b623-4d93-87a4-d23bf411294c)
133140
- [x] [IF](https://support.office.com/en-us/article/if-function-69aed7c9-4e8a-4755-a9bc-aa8bbff73be2)
134141

@@ -145,7 +152,6 @@ In addition we have special functions for accessing the data in context: `OBJREF
145152
- [ ] [ADDRESS](https://support.office.com/en-us/article/address-function-d0c26c0d-3991-446b-8de4-ab46431d4f89)
146153
- [ ] [AMORDEGRC](https://support.office.com/en-us/article/amordegrc-function-a14d0ca1-64a4-42eb-9b3d-b0dededf9e51)
147154
- [ ] [AMORLINC](https://support.office.com/en-us/article/amorlinc-function-7d417b45-f7f5-4dba-a0a5-3451a81079a8)
148-
- [ ] [ARABIC](https://support.office.com/en-us/article/arabic-function-9a8da418-c17b-4ef9-a657-9370a30a674f)
149155
- [ ] [AREAS](https://support.office.com/en-us/article/areas-function-8392ba32-7a41-43b3-96b0-3695d2ec6152)
150156
- [ ] [ASC](https://support.office.com/en-us/article/asc-function-0b6abf1c-c663-4004-a964-ebc00b723266)
151157
- [ ] [ASIN](https://support.office.com/en-us/article/asin-function-81fb95e5-6d6f-48c4-bc45-58f955c6d347)
@@ -517,7 +523,6 @@ In addition we have special functions for accessing the data in context: `OBJREF
517523
- [ ] [SUMXMY2](https://support.office.com/en-us/article/sumxmy2-function-9d144ac1-4d79-43de-b524-e2ecee23b299)
518524
- [ ] [SWITCH](https://support.office.com/en-us/article/switch-function-47ab33c0-28ce-4530-8a45-d532ec4aa25e)
519525
- [ ] [SYD](https://support.office.com/en-us/article/syd-function-069f8106-b60b-4ca2-98e0-2a0f206bdb27)
520-
- [ ] [T](https://support.office.com/en-us/article/t-function-fb83aeec-45e7-4924-af95-53e073541228)
521526
- [ ] [TAN](https://support.office.com/en-us/article/tan-function-08851a40-179f-4052-b789-d7f699447401)
522527
- [ ] [TANH](https://support.office.com/en-us/article/tanh-function-017222f0-a0c3-4f69-9787-b3202295dc6c)
523528
- [ ] [TBILLEQ](https://support.office.com/en-us/article/tbilleq-function-2ab72d90-9b4d-4efe-9fc2-0f81f2c19c8c)
@@ -528,7 +533,6 @@ In addition we have special functions for accessing the data in context: `OBJREF
528533
- [ ] [T.DIST.RT](https://support.office.com/en-us/article/tdistrt-function-20a30020-86f9-4b35-af1f-7ef6ae683eda)
529534
- [ ] [TDIST](https://support.office.com/en-us/article/tdist-function-630a7695-4021-4853-9468-4a1f9dcdd192)
530535
- [ ] [TEXT](https://support.office.com/en-us/article/text-function-20d5ac4d-7b94-49fd-bb38-93d29371225c)
531-
- [ ] [TEXTJOIN](https://support.office.com/en-us/article/textjoin-function-357b449a-ec91-49d0-80c3-0e8fc845691c)
532536
- [ ] [TIME](https://support.office.com/en-us/article/time-function-9a5aff99-8f7d-4611-845e-747d0b8d5457)
533537
- [ ] [TIMEVALUE](https://support.office.com/en-us/article/timevalue-function-0b615c12-33d8-4431-bf3d-f3eb6d186645)
534538
- [ ] [T.INV](https://support.office.com/en-us/article/tinv-function-2908272b-4e61-4942-9df9-a25fec9b0e2e)
@@ -545,7 +549,6 @@ In addition we have special functions for accessing the data in context: `OBJREF
545549
- [ ] [TYPE](https://support.office.com/en-us/article/type-function-45b4e688-4bc3-48b3-a105-ffa892995899)
546550
- [ ] [UNICHAR](https://support.office.com/en-us/article/unichar-function-ffeb64f5-f131-44c6-b332-5cd72f0659b8)
547551
- [ ] [UNICODE](https://support.office.com/en-us/article/unicode-function-adb74aaa-a2a5-4dde-aff6-966e4e81f16f)
548-
- [ ] [VALUE](https://support.office.com/en-us/article/value-function-257d0108-07dc-437d-ae1c-bc2d3953d8c2)
549552
- [ ] [VAR](https://support.office.com/en-us/article/var-function-1f2b7ab2-954d-4e17-ba2c-9e58b15a7da2)
550553
- [ ] [VAR.P](https://support.office.com/en-us/article/varp-function-73d1285c-108c-4843-ba5d-a51f90656f3a)
551554
- [ ] [VAR.S](https://support.office.com/en-us/article/vars-function-913633de-136b-449d-813e-65a00b2b990b)

deps.edn

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
:test {:extra-paths ["test"]
2020
:extra-deps {org.clojure/test.check {:mvn/version "RELEASE"}}}
2121

22-
:coverage {:extra-deps {cloverage {:mvn/version "1.0.11"}}
22+
:coverage {:extra-deps {cloverage {:mvn/version "RELEASE"}}
2323
:main-opts ["-m" "cloverage.coverage"
2424
"--src-ns-path" "src"
2525
"--test-ns-path" "test"

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<modelVersion>4.0.0</modelVersion>
44
<groupId>io.xapix</groupId>
55
<artifactId>axel-f</artifactId>
6-
<version>0.2.1</version>
6+
<version>0.2.2-SNAPSHOT</version>
77
<name>axel-f</name>
88
<dependencies>
99
<dependency>

release-js/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "axel-f",
3-
"version": "0.2.1",
3+
"version": "0.2.2-SNAPSHOT",
44
"description": "axel-f is an engine that can evaluate excel-like expressions.",
55
"homepage": "https://github.com/xapix-io/axel-f",
66
"author": "Kirill Chernyshov (https://github.com/DeLaGuardo)",

src/axel_f/error.cljc

+6
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@
1313
arg-position
1414
(when arg-position " ")
1515
"expects number values. But '" value "' is a text and cannot be coerced to a number."))
16+
17+
(defn format-not-a-string-error [fnname arg-position value]
18+
(str "Function " fnname " parameter "
19+
arg-position
20+
(when arg-position " ")
21+
"expects text values. But '" value "' is a number and cannot be coerced to a string."))

src/axel_f/functions.cljc

+40-2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@
6666
:desc "Returns the text with the non-printable ASCII characters removed."
6767
:args [{:desc "The text whose non-printable characters are to be removed."}]}
6868

69+
"ARABIC" {:impl #'text/arabic-fn
70+
:desc "Computes the value of a Roman numeral."
71+
:args [{:desc "The Roman numeral to format, whose value must be between 1 and 3999, inclusive."}]}
72+
6973
"CHAR" {:impl #'text/char-fn
7074
:desc "Convert a number into a character according to the current Unicode table."
7175
:args [{:desc "The number of the character to look up from the current Unicode table in decimal format."}]}
@@ -74,7 +78,7 @@
7478
:desc "Returns the numeric Unicode map value of the first character in the string provided."
7579
:args [{:desc "The string whose first character's Unicode map value will be returned."}]}
7680

77-
"CONCATENATE" {:impl (partial text/join-fn "")
81+
"CONCATENATE" {:impl (partial text/textjoin-fn "" false)
7882
:desc "Appends strings to one another."
7983
:args [{:desc "The initial string."}
8084
{:desc "More strings to append in sequence."
@@ -99,7 +103,8 @@
99103
{:desc "The character within arg2 at which to start the search."
100104
:opt true}]}
101105

102-
"JOIN" {:impl #'text/join-fn
106+
"JOIN" {:impl (fn [delimeter & args]
107+
(apply text/textjoin-fn delimeter false args))
103108
:desc "Concatenates the elements of one or more one-dimensional arrays using a specified delimiter."
104109
:args [{:desc "The character or string to place between each concatenated value."}
105110
{:desc "The value or values to be appended using arg1."}
@@ -131,6 +136,22 @@
131136
:desc "Capitalizes each word in a specified string."
132137
:args [{:desc "The text which will be returned with the first letter of each word in uppercase and all other letters in lowercase."}]}
133138

139+
"REGEXEXTRACT" {:impl #'text/regexextract-fn
140+
:desc "Extracts matching substrings according to a regular expression."
141+
:args [{:desc "The input text."}
142+
{:desc "The first part of arg1 that matches this expression will be returned."}]}
143+
144+
"REGEXMATCH" {:impl #'text/regexmatch-fn
145+
:desc "Whether a piece of text matches a regular expression."
146+
:args [{:desc "The text to be tested against the regular expression."}
147+
{:desc "The regular expression to test the text against."}]}
148+
149+
"REGEXREPLACE" {:impl #'text/regexreplace-fn
150+
:desc "Replaces part of a text string with a different text string using regular expressions."
151+
:args [{:desc "The text, a part of which will be replaced."}
152+
{:desc "The regular expression. All matching instances in text will be replaced."}
153+
{:desc "The text which will be inserted into the original text."}]}
154+
134155
"REPLACE" {:impl #'text/replace-fn
135156
:desc "Replaces part of a text string with a different text string."
136157
:args [{:desc "The text, a part of which will be replaced."}
@@ -177,6 +198,10 @@
177198
{:desc "The instance of arg2 within arg1 to replace with arg3. By default, all occurrences of arg2 are replaced; however, if arg4 is specified, only the indicated instance of arg2 is replaced."
178199
:opt true}]}
179200

201+
"T" {:impl #'text/t-fn
202+
:desc "Returns string arguments as text."
203+
:args [{:desc "The argument to be converted to text."}]}
204+
180205
"TRIM" {:impl #'text/trim-fn
181206
:desc "Removes leading, trailing, and repeated spaces in text."
182207
:args [{:desc "The text or reference to a cell containing text to be trimmed."}]}
@@ -185,6 +210,19 @@
185210
:desc "Converts a specified string to uppercase."
186211
:args [{:desc "The string to convert to uppercase."}]}
187212

213+
"VALUE" {:impl #'text/value-fn
214+
:desc "Converts a string in any of the date, time or number formats that axel-f understands into a number."
215+
:args [{:desc "The string containing the value to be converted."}]}
216+
217+
"TEXTJOIN" {:impl #'text/textjoin-fn
218+
:desc "Combines the text from multiple strings and/or arrays, with a specifiable delimiter separating the different texts."
219+
:args [{:desc "A string, possibly empty, or a reference to a valid string. If empty, text will be simply concatenated."}
220+
{:desc "A boolean; if TRUE, empty strings selected in the text arguments won't be included in the result."}
221+
{:desc "Any text item. This could be a string, or an array of strings in a range."}
222+
{:desc "Additional text item(s)."
223+
:opt true
224+
:repeatable true}]}
225+
188226
"COUNT" {:impl #'stat/count-fn
189227
:desc "Returns a count of the number of numeric values in a dataset."
190228
:args [{:desc "The first value or range to consider when counting."}

src/axel_f/functions/text.cljc

+88-25
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,29 @@
2121
(or (get cmap pattern) pattern)
2222
(string/escape pattern cmap))))
2323

24-
;; TODO
25-
;; (defn arabic-fn [roman-numeral])
24+
(def roman-numerals
25+
{\I 1 \V 5 \X 10 \L 50
26+
\C 100 \D 500 \M 1000 "IV" 4
27+
"IX" 9 "XL" 40 "XC" 90 "CD" 400
28+
"CM" 900})
29+
30+
(defn arabic-fn [s]
31+
(let [sign (if (string/starts-with? s "-")
32+
-1 1)
33+
s (partition-all 2 (vec (string/replace-first s #"-" "")))]
34+
(loop [acc 0 current (first s) other (rest s)]
35+
(if current
36+
(let [is-pair? (get roman-numerals (apply str current))
37+
value (or is-pair?
38+
(get roman-numerals (first current)))]
39+
(if is-pair?
40+
(recur (+ acc value) (first other) (rest other))
41+
(let [new-other (->> (concat current other)
42+
flatten
43+
rest
44+
(partition-all 2))]
45+
(recur (+ acc value) (first new-other) (rest new-other)))))
46+
(* sign acc)))))
2647

2748
;; TODO
2849
;; (defn asc-fn [])
@@ -69,12 +90,6 @@
6990
;; TODO
7091
;; (defn fixed-fn [])
7192

72-
(defn join-fn [delimeter & items]
73-
(->> items
74-
flatten
75-
(map coercion/excel-str)
76-
(string/join delimeter)))
77-
7893
(defn left-fn
7994
([text] (left-fn text 1))
8095
([text number]
@@ -113,14 +128,53 @@
113128
(defn proper-fn [text]
114129
(string/replace (coercion/excel-str text) #"\w*" string/capitalize))
115130

116-
;; TODO
117-
;; (defn regexextract-fn [])
131+
(defn regexextract-fn [text regular-expression]
132+
(cond
133+
(not (string? text))
134+
(throw (error/error "#VALUE!"
135+
(error/format-not-a-string-error "REGEXEXTRACT" 1 text)))
118136

119-
;; TODO
120-
;; (defn regexmatch-fn [])
137+
(not (string? regular-expression))
138+
(throw (error/error "#VALUE!"
139+
(error/format-not-a-string-error "REGEXEXTRACT" 2 regular-expression)))
140+
141+
:otherwise
142+
(let [res (re-find (re-pattern regular-expression)
143+
text)]
144+
(cond
145+
(string? res) res
146+
(vector? res) (second res)
147+
:otherwise res))))
148+
149+
(defn regexmatch-fn [text regular-expression]
150+
(cond
151+
(not (string? text))
152+
(throw (error/error "#VALUE!"
153+
(error/format-not-a-string-error "REGEXMATCH" 1 text)))
121154

122-
;; TODO
123-
;; (defn regexreplace-fn [])
155+
(not (string? regular-expression))
156+
(throw (error/error "#VALUE!"
157+
(error/format-not-a-string-error "REGEXMATCH" 2 regular-expression)))
158+
159+
:otherwise
160+
(boolean (regexextract-fn text regular-expression))))
161+
162+
(defn regexreplace-fn [text regular-expression replacement]
163+
(cond
164+
(not (string? text))
165+
(throw (error/error "#VALUE!"
166+
(error/format-not-a-string-error "REGEXREPLACE" 1 text)))
167+
168+
(not (string? regular-expression))
169+
(throw (error/error "#VALUE!"
170+
(error/format-not-a-string-error "REGEXREPLACE" 2 regular-expression)))
171+
172+
(not (string? replacement))
173+
(throw (error/error "#VALUE!"
174+
(error/format-not-a-string-error "REGEXREPLACE" 3 replacement)))
175+
176+
:otherwise
177+
(string/replace text (re-pattern regular-expression) replacement)))
124178

125179
(defn replace-fn
126180
[text position length new-text]
@@ -155,11 +209,7 @@
155209
(if-let [n (coercion/excel-number n)]
156210
(if (<= 0 n 3999)
157211
(let [n (int n)
158-
alphabet (sort-by val >
159-
{\I 1 \V 5 \X 10 \L 50
160-
\C 100 \D 500 \M 1000 "IV" 4
161-
"IX" 9 "XL" 40 "XC" 90 "CD" 400
162-
"CM" 900})]
212+
alphabet (sort-by val > roman-numerals)]
163213
(loop [res "" n n]
164214
(if (zero? n) res
165215
(let [[rom arab] (some #(when (<= (val %) n) %) alphabet)]
@@ -215,8 +265,9 @@
215265
(throw (error/error "#VALUE!"
216266
(error/format-not-a-number-error "SUBSTITUTE" 4 occurrence)))))
217267

218-
;; TODO
219-
;; (defn t-fn [])
268+
(defn t-fn [value]
269+
(when (string? value)
270+
value))
220271

221272
;; TODO
222273
;; (defn text-fn [])
@@ -231,8 +282,20 @@
231282
coercion/excel-str
232283
string/upper-case))
233284

234-
;; TODO
235-
;; (defn value-fn [])
285+
(defn value-fn [s]
286+
(or
287+
(when (and (seqable? s)
288+
(empty? s))
289+
0)
290+
(when-not (boolean? s)
291+
(coercion/excel-number s))
292+
(throw (error/error "#VALUE!" (str "VALUE parameter '" (coercion/excel-str s) "' cannot be parsed to number.")))))
236293

237-
;; TODO
238-
;; (defn textjoin-fn [])
294+
(defn textjoin-fn [delimeter ignore-empty & items]
295+
(->> items
296+
flatten
297+
(map coercion/excel-str)
298+
(filter (if ignore-empty
299+
not-empty
300+
identity))
301+
(string/join delimeter)))

0 commit comments

Comments
 (0)