|
| 1 | +# dictionaries |
1 | 2 | include(joinpath(@__DIR__, "en", "standard_dictionary_numbers_extended.jl")) |
2 | 3 | include(joinpath(@__DIR__, "en", "large_standard_dictionary_numbers_extended.jl")) |
3 | 4 | include(joinpath(@__DIR__, "en", "ordinal_dictionaries.jl")) |
| 5 | + |
| 6 | +# utils |
| 7 | +include(joinpath(@__DIR__, "en", "utils.jl")) |
4 | 8 |
|
5 | 9 | # convert a value < 100 to English. |
6 | | -function small_convert_en(number::Integer; british::Bool = false, dict::Symbol = :modern) |
7 | | - scale_numbers = _scale_modern # define scale type |
| 10 | +function _small_convert_en!(io::IOBuffer, number::Integer) |
8 | 11 | if number < 20 |
9 | | - word = _small_numbers[number + 1] |
10 | | - |
11 | | - return word |
| 12 | + print(io, _small_numbers[number + 1]) |
| 13 | + return io |
12 | 14 | end |
13 | 15 |
|
14 | | - v = 0 |
15 | | - while v < length(_tens) |
16 | | - d_cap = _tens[v + 1] |
17 | | - d_number = BigInt(20 + 10 * v) |
| 16 | + m = mod(number, 10) |
| 17 | + |
| 18 | + for (v, d̂) in enumerate(_tens) |
| 19 | + d = 20 + 10 * (v - 1) |
18 | 20 |
|
19 | | - if d_number + 10 > number |
20 | | - if mod(number, 10) ≠ 0 |
21 | | - word = d_cap * "-" * _small_numbers[mod(number, 10) + 1] |
22 | | - |
23 | | - return word |
| 21 | + if d + 10 > number |
| 22 | + if m ≠ 0 |
| 23 | + n = _small_numbers[m + 1] |
| 24 | + print(io, d̂, '-', n) |
| 25 | + return io |
24 | 26 | end |
25 | | - |
26 | | - return d_cap |
| 27 | + print(io, d̂) |
| 28 | + return io |
27 | 29 | end |
28 | | - v += 1 |
29 | 30 | end |
| 31 | + |
| 32 | + return io |
| 33 | +end |
| 34 | + |
| 35 | +function small_convert_en(number::Integer) |
| 36 | + word_buf = IOBuffer() |
| 37 | + _small_convert_en!(word_buf, number) |
| 38 | + return String(take!(word_buf)) |
30 | 39 | end |
31 | 40 |
|
32 | 41 | # convert a value < 1000 to english, special cased because it is the level that excludes |
33 | 42 | # the < 100 special case. The rest are more general. This also allows you to get |
34 | 43 | # strings in the form of "forty-five hundred" if called directly. |
35 | | -function large_convert_en(number::Integer; british::Bool = false, dict::Symbol = :modern) |
36 | | - scale_numbers = _scale_modern # define scale type |
37 | | - word = string() # initiate empty string |
| 44 | +function _large_convert_en!(io::IOBuffer, number::Integer; british::Bool = false) |
38 | 45 | divisor = div(number, 100) |
39 | 46 | modulus = mod(number, 100) |
40 | 47 |
|
41 | 48 | if divisor > 0 |
42 | | - word = _small_numbers[divisor + 1] * " hundred" |
| 49 | + print(io, _small_numbers[divisor + 1], " hundred") |
43 | 50 | if modulus > 0 |
44 | | - word = word * " " |
| 51 | + print(io, ' ') |
45 | 52 | end |
46 | 53 | end |
47 | 54 |
|
48 | 55 | if british |
49 | | - if ! iszero(divisor) && ! iszero(modulus) |
50 | | - word = word * "and " |
| 56 | + if !iszero(divisor) && !iszero(modulus) |
| 57 | + print(io, "and ") |
51 | 58 | end |
52 | 59 | end |
53 | 60 |
|
54 | 61 | if modulus > 0 |
55 | | - word = word * small_convert_en(modulus, british=british, dict=dict) |
| 62 | + _small_convert_en!(io, modulus) |
56 | 63 | end |
57 | 64 |
|
58 | | - return word |
| 65 | + return io |
59 | 66 | end |
60 | 67 |
|
61 | | -function spelled_out_en(number::Integer; british::Bool = false, dict::Symbol = :modern) |
| 68 | +function large_convert_en(number::Integer; british::Bool = false) |
| 69 | + word_buf = IOBuffer() |
| 70 | + _large_convert_en!(word_buf, number; british = british) |
| 71 | + return String(take!(word_buf)) |
| 72 | +end |
| 73 | + |
| 74 | +function _spelled_out_en!(io::IOBuffer, number_norm::Integer; british::Bool = false, dict::Symbol = :modern) |
62 | 75 | scale_numbers = _scale_modern # default to :modern |
63 | 76 | if isequal(dict, :british) |
64 | 77 | scale_numbers = _scale_traditional_british |
65 | 78 | elseif isequal(dict, :european) |
66 | 79 | scale_numbers = _scale_traditional_european |
67 | 80 | elseif isequal(dict, :modern) |
| 81 | + # This is the default condition |
68 | 82 | else |
69 | | - throw(error("unrecognized dict value: $dict")) |
| 83 | + error("Unrecognized dict value: $dict") |
70 | 84 | end |
71 | 85 |
|
72 | | - number = big(number) |
73 | | - isnegative = false |
74 | | - if number < 0 |
75 | | - isnegative = true |
| 86 | + if number_norm < 0 |
| 87 | + print(io, "negative ") |
76 | 88 | end |
77 | | - |
78 | | - number = abs(number) |
79 | | - if number > limit - 1 |
80 | | - throw(error("""SpelledOut.jl does not support numbers larger than $(limit_str * " - 1"). Sorry about that!""")) |
| 89 | + |
| 90 | + if number_norm > limit - 1 |
| 91 | + error("""SpelledOut.jl does not support numbers larger than $(limit_str * " - 1"). Sorry about that!""") |
81 | 92 | end |
82 | 93 |
|
83 | | - if number < 100 |
84 | | - word = small_convert_en(number, british=british, dict=dict) |
85 | | - |
86 | | - if isnegative |
87 | | - word = "negative " * word |
88 | | - end |
89 | | - |
90 | | - return word |
| 94 | + |
| 95 | + if number_norm < 100 |
| 96 | + _small_convert_en!(io, number_norm) |
| 97 | + return io |
91 | 98 | end |
92 | 99 |
|
93 | | - if number < 1000 |
94 | | - word = large_convert_en(number, british=british, dict=dict) |
95 | | - |
96 | | - if isnegative |
97 | | - word = "negative " * word |
98 | | - end |
99 | | - |
100 | | - return word |
| 100 | + if number_norm < 1000 |
| 101 | + _large_convert_en!(io, number_norm, british = british) |
| 102 | + return io |
101 | 103 | end |
102 | 104 |
|
103 | | - v = 0 |
104 | | - while v ≤ length(scale_numbers) |
| 105 | + number = abs(number_norm) |
| 106 | + |
| 107 | + for v in 0:length(scale_numbers) |
105 | 108 | d_idx = v |
106 | | - d_number = BigInt(round(big(1000)^v)) |
| 109 | + d_number = round(big(1000)^v) |
107 | 110 |
|
108 | 111 | if d_number > number |
109 | | - modulus = BigInt(big(1000)^(d_idx - 1)) |
| 112 | + modulus = big(1000)^(d_idx - 1) |
110 | 113 | l, r = divrem(number, modulus) |
111 | | - word = large_convert_en(l, british=british, dict=dict) * " " * scale_numbers[d_idx - 1] |
112 | 114 |
|
| 115 | + _large_convert_en!(io, l, british = british) |
| 116 | + print(io, " ", scale_numbers[d_idx - 1]) |
113 | 117 | if r > 0 |
114 | | - word = word * ", " * spelled_out_en(r, british=british, dict=dict) |
| 118 | + print(io, ", ") |
| 119 | + _spelled_out_en!(io, r, british = british, dict = dict) |
115 | 120 | end |
116 | 121 |
|
117 | | - if isnegative |
118 | | - word = "negative " * word |
119 | | - end |
120 | | - |
121 | | - return word |
| 122 | + return io |
122 | 123 | end |
123 | | - |
124 | | - v += 1 |
125 | 124 | end |
| 125 | + |
| 126 | + error("Unreachable") |
| 127 | +end |
| 128 | + |
| 129 | +function spelled_out_en(number_orig::Integer; british::Bool = false, dict::Symbol = :modern) |
| 130 | + word_buf = IOBuffer() |
| 131 | + _spelled_out_en!(word_buf, number_orig; british = british, dict = dict) |
| 132 | + return String(take!(word_buf)) |
126 | 133 | end |
127 | 134 |
|
128 | 135 | # Need to print ordinal numbers for the irrational printing |
129 | 136 | function spell_ordinal_en(number::Integer; british::Bool = false, dict::Symbol = :modern) |
130 | | - s = spelled_out_en(number, british = british, dict = dict) |
131 | | - |
132 | | - lastword = split(s)[end] |
133 | | - redolast = split(lastword, "-")[end] |
| 137 | + word_buf = IOBuffer() |
| 138 | + _spelled_out_en!(word_buf, number, british = british, dict = dict) |
| 139 | + s = String(take!(word_buf)) |
| 140 | + |
| 141 | + lastword = lastsplit(isspace, s) |
| 142 | + redolast = lastsplit('-', lastword) |
134 | 143 |
|
135 | 144 | if redolast != lastword |
136 | | - lastsplit = "-" |
| 145 | + _lastsplit = '-' |
137 | 146 | word = redolast |
138 | 147 | else |
139 | | - lastsplit = " " |
| 148 | + _lastsplit = ' ' |
140 | 149 | word = lastword |
141 | 150 | end |
| 151 | + |
| 152 | + firstpart = firstlastsplit(_lastsplit, s) |
142 | 153 |
|
143 | | - firstpart = reverse(split(reverse(s), lastsplit, limit = 2)[end]) |
144 | | - firstpart = (firstpart == word) ? string() : firstpart * lastsplit |
145 | | - |
| 154 | + if firstpart != word |
| 155 | + print(word_buf, firstpart, _lastsplit) |
| 156 | + end |
| 157 | + |
146 | 158 | if haskey(irregular, word) |
147 | | - word = irregular[word] |
| 159 | + print(word_buf, irregular[word]) |
148 | 160 | elseif word[end] == 'y' |
149 | | - word = word[1:end-1] * ysuffix |
| 161 | + print(word_buf, word[1:end-1], ysuffix) |
150 | 162 | else |
151 | | - word = word * suffix |
| 163 | + print(word_buf, word, suffix) |
152 | 164 | end |
153 | | - |
154 | | - return firstpart * word |
| 165 | + |
| 166 | + return String(take!(word_buf)) |
155 | 167 | end |
156 | 168 |
|
157 | 169 | # This method is an internal method used for spelling out floats |
158 | 170 | function decimal_convert_en(number::AbstractString; british::Bool = false, dict::Symbol = :modern) |
159 | 171 | # decimal, whole = modf(number) |
160 | 172 | # whole = round(BigInt, whole) |
161 | 173 | whole, decimal = split(number, ".") |
162 | | - word = spelled_out_en(parse(BigInt, whole), british=british, dict=dict) * string(" point") |
163 | | - # word = spelled_out_en(whole, british=british, dict=dict) * string(" point") |
| 174 | + word_buf = IOBuffer() |
| 175 | + _spelled_out_en!(word_buf, parse(BigInt, whole), british = british, dict = dict) |
| 176 | + print(word_buf, " point") |
164 | 177 |
|
165 | 178 | for i in decimal |
166 | | - word = word * " " * _small_number_dictionary[i] |
| 179 | + print(word_buf, ' ', _small_number_dictionary[i]) |
167 | 180 | end |
168 | 181 |
|
169 | | - return word |
| 182 | + return String(take!(word_buf)) |
170 | 183 | end |
171 | 184 |
|
172 | 185 | function spelled_out_en(number::AbstractFloat; british::Bool = false, dict::Symbol = :modern) |
@@ -198,19 +211,19 @@ end |
198 | 211 |
|
199 | 212 | # Spell out complex numbers |
200 | 213 | function spelled_out_en(number::Complex; british::Bool = false, dict::Symbol = :modern) |
201 | | - return spelled_out_en(real(number), british = british, dict = dict) * " and " * spelled_out_en(imag(number), british = british, dict=dict) * " imaginaries" |
| 214 | + return spelled_out_en(real(number), british = british, dict = dict) * " and " * spelled_out_en(imag(number), british = british, dict = dict) * " imaginaries" |
202 | 215 | end |
203 | 216 |
|
204 | 217 | function spelled_out_en(number::Rational; british::Bool = false, dict::Symbol = :modern) |
205 | | - _num, _den = number.num, number.den |
206 | | - |
207 | | - # return the number itself if the denomimator is one |
208 | | - isone(_den) && return spelled_out_en(_num, british = british, dict = dict) |
209 | | - |
210 | | - word = spelled_out_en(_num, british = british, dict = dict) * " " * spell_ordinal_en(_den, british = british, dict = dict) |
211 | | - |
212 | | - # account for pluralisation |
213 | | - return isone(_num) ? word : word * "s" |
| 218 | + _num, _den = number.num, number.den |
| 219 | + |
| 220 | + # return the number itself if the denomimator is one |
| 221 | + isone(_den) && return spelled_out_en(_num, british = british, dict = dict) |
| 222 | + |
| 223 | + word = spelled_out_en(_num, british = british, dict = dict) * " " * spell_ordinal_en(_den, british = british, dict = dict) |
| 224 | + |
| 225 | + # account for pluralisation |
| 226 | + return isone(_num) ? word : word * "s" |
214 | 227 | end |
215 | 228 |
|
216 | 229 | function spelled_out_en(number::AbstractIrrational; british::Bool = false, dict::Symbol = :modern) |
|
0 commit comments