Skip to content

Commit 0d26536

Browse files
committed
Add lazy string type
1 parent 42e9490 commit 0d26536

File tree

9 files changed

+121
-71
lines changed

9 files changed

+121
-71
lines changed

base/Base.jl

+4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ include("refpointer.jl")
9494
include("checked.jl")
9595
using .Checked
9696

97+
# Lazy strings
98+
include("strings/lazy.jl")
99+
97100
# array structures
98101
include("indices.jl")
99102
include("array.jl")
@@ -154,6 +157,7 @@ include("dict.jl")
154157
include("abstractset.jl")
155158
include("set.jl")
156159

160+
# Strings
157161
include("char.jl")
158162
include("strings/basic.jl")
159163
include("strings/string.jl")

base/abstractarray.jl

+20-14
Original file line numberDiff line numberDiff line change
@@ -740,19 +740,21 @@ end
740740
# copy from an some iterable object into an AbstractArray
741741
function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer)
742742
if (sstart < 1)
743-
throw(ArgumentError(string("source start offset (",sstart,") is < 1")))
743+
throw(ArgumentError(LazyString("source start offset (",sstart,") is < 1")))
744744
end
745745
y = iterate(src)
746746
for j = 1:(sstart-1)
747747
if y === nothing
748-
throw(ArgumentError(string("source has fewer elements than required, ",
749-
"expected at least ",sstart,", got ",j-1)))
748+
throw(ArgumentError(LazyString(
749+
"source has fewer elements than required, ",
750+
"expected at least ", sstart,", got ", j-1)))
750751
end
751752
y = iterate(src, y[2])
752753
end
753754
if y === nothing
754-
throw(ArgumentError(string("source has fewer elements than required, ",
755-
"expected at least ",sstart,", got ",sstart-1)))
755+
throw(ArgumentError(LazyString(
756+
"source has fewer elements than required, ",
757+
"expected at least ",sstart," got ", sstart-1)))
756758
end
757759
i = Int(dstart)
758760
while y !== nothing
@@ -766,19 +768,22 @@ end
766768

767769
# this method must be separate from the above since src might not have a length
768770
function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer, n::Integer)
769-
n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative")))
771+
n < 0 && throw(ArgumentError(LazyString("tried to copy n=",n,
772+
", elements, but n should be nonnegative")))
770773
n == 0 && return dest
771774
dmax = dstart + n - 1
772775
inds = LinearIndices(dest)
773776
if (dstart inds || dmax inds) | (sstart < 1)
774-
sstart < 1 && throw(ArgumentError(string("source start offset (",sstart,") is < 1")))
777+
sstart < 1 && throw(ArgumentError(LazyString("source start offset (",
778+
sstart,") is < 1")))
775779
throw(BoundsError(dest, dstart:dmax))
776780
end
777781
y = iterate(src)
778782
for j = 1:(sstart-1)
779783
if y === nothing
780-
throw(ArgumentError(string("source has fewer elements than required, ",
781-
"expected at least ",sstart,", got ",j-1)))
784+
throw(ArgumentError(LazyString(
785+
"source has fewer elements than required, ",
786+
"expected at least ",sstart,", got ",j-1)))
782787
end
783788
y = iterate(src, y[2])
784789
end
@@ -834,7 +839,8 @@ function copyto!(dest::AbstractArray, dstart::Integer,
834839
src::AbstractArray, sstart::Integer,
835840
n::Integer)
836841
n == 0 && return dest
837-
n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative")))
842+
n < 0 && throw(ArgumentError(LazyString("tried to copy n=",
843+
n," elements, but n should be nonnegative")))
838844
destinds, srcinds = LinearIndices(dest), LinearIndices(src)
839845
(checkbounds(Bool, destinds, dstart) && checkbounds(Bool, destinds, dstart+n-1)) || throw(BoundsError(dest, dstart:dstart+n-1))
840846
(checkbounds(Bool, srcinds, sstart) && checkbounds(Bool, srcinds, sstart+n-1)) || throw(BoundsError(src, sstart:sstart+n-1))
@@ -852,12 +858,12 @@ end
852858
function copyto!(B::AbstractVecOrMat{R}, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int},
853859
A::AbstractVecOrMat{S}, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) where {R,S}
854860
if length(ir_dest) != length(ir_src)
855-
throw(ArgumentError(string("source and destination must have same size (got ",
856-
length(ir_src)," and ",length(ir_dest),")")))
861+
throw(ArgumentError(LazyString("source and destination must have same size (got ",
862+
length(ir_src)," and ",length(ir_dest),")")))
857863
end
858864
if length(jr_dest) != length(jr_src)
859-
throw(ArgumentError(string("source and destination must have same size (got ",
860-
length(jr_src)," and ",length(jr_dest),")")))
865+
throw(ArgumentError(LazyString("source and destination must have same size (got ",
866+
length(jr_src)," and ",length(jr_dest),")")))
861867
end
862868
@boundscheck checkbounds(B, ir_dest, jr_dest)
863869
@boundscheck checkbounds(A, ir_src, jr_src)

base/compiler/compiler.jl

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ add_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_sadd_int(x, y)
6060
add_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_uadd_int(x, y)
6161
add_with_overflow(x::Bool, y::Bool) = (x+y, false)
6262

63+
include("strings/lazy.jl")
64+
6365
# core array operations
6466
include("indices.jl")
6567
include("array.jl")

base/exports.jl

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export
5454
IOStream,
5555
LinRange,
5656
Irrational,
57+
LazyString,
5758
Matrix,
5859
MergeSort,
5960
Missing,
@@ -938,6 +939,7 @@ export
938939
@s_str, # regex substitution string
939940
@v_str, # version number
940941
@raw_str, # raw string with no interpolation/unescaping
942+
@lazy_str, # lazy string
941943

942944
# documentation
943945
@text_str,

base/math.jl

+6-5
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,14 @@ using Core.Intrinsics: sqrt_llvm
3030
using .Base: IEEEFloat
3131

3232
@noinline function throw_complex_domainerror(f::Symbol, x)
33-
throw(DomainError(x, string("$f will only return a complex result if called with a ",
34-
"complex argument. Try $f(Complex(x)).")))
33+
throw(DomainError(x,
34+
LazyString(f," will only return a complex result if called with a complex argument. Try ", f,"(Complex(x)).")))
3535
end
3636
@noinline function throw_exp_domainerror(x)
37-
throw(DomainError(x, string("Exponentiation yielding a complex result requires a ",
38-
"complex argument.\nReplace x^y with (x+0im)^y, ",
39-
"Complex(x)^y, or similar.")))
37+
throw(DomainError(x, LazyString(
38+
"Exponentiation yielding a complex result requires a ",
39+
"complex argument.\nReplace x^y with (x+0im)^y, ",
40+
"Complex(x)^y, or similar.")))
4041
end
4142

4243
# non-type specific math functions

base/strings/lazy.jl

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""
2+
LazyString <: AbstractString
3+
4+
A lazy representation of string interpolation. This is useful when a string
5+
needs to be constructed in a context where performing the actual interpolation
6+
and string construction is unnecessary or undesirable (e.g. in error paths
7+
of of functions).
8+
9+
This type is designed to be cheap to construct at runtime, trying to offload
10+
as much work as possible to either the macro or later printing operations.
11+
"""
12+
struct LazyString <: AbstractString
13+
parts::Tuple
14+
LazyString(args...) = new(args)
15+
end
16+
17+
macro lazy_str(string)
18+
parts = Any[]
19+
lastidx = idx = 1
20+
while (idx = findnext('$', string, idx)) !== nothing
21+
lastidx < idx && push!(parts, string[lastidx:idx-1])
22+
idx += 1
23+
expr, idx = Meta.parse(string, idx; greedy=false, raise=false)
24+
push!(parts, esc(expr))
25+
lastidx = idx
26+
end
27+
lastidx <= lastindex(string) && push!(parts, string[lastidx:end])
28+
:(LazyString($(parts...)))
29+
end
30+
31+
function print(io::IO, s::LazyString)
32+
for part in s.parts
33+
print(io, part)
34+
end
35+
end

0 commit comments

Comments
 (0)