Skip to content

Commit 1283a39

Browse files
committed
Add lazy string type
1 parent c5158f4 commit 1283a39

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
@@ -123,6 +123,9 @@ include("refpointer.jl")
123123
include("checked.jl")
124124
using .Checked
125125

126+
# Lazy strings
127+
include("strings/lazy.jl")
128+
126129
# array structures
127130
include("indices.jl")
128131
include("array.jl")
@@ -200,6 +203,7 @@ include("dict.jl")
200203
include("abstractset.jl")
201204
include("set.jl")
202205

206+
# Strings
203207
include("char.jl")
204208
include("strings/basic.jl")
205209
include("strings/string.jl")

base/abstractarray.jl

+20-14
Original file line numberDiff line numberDiff line change
@@ -914,19 +914,21 @@ end
914914
# copy from an some iterable object into an AbstractArray
915915
function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer)
916916
if (sstart < 1)
917-
throw(ArgumentError(string("source start offset (",sstart,") is < 1")))
917+
throw(ArgumentError(LazyString("source start offset (",sstart,") is < 1")))
918918
end
919919
y = iterate(src)
920920
for j = 1:(sstart-1)
921921
if y === nothing
922-
throw(ArgumentError(string("source has fewer elements than required, ",
923-
"expected at least ",sstart,", got ",j-1)))
922+
throw(ArgumentError(LazyString(
923+
"source has fewer elements than required, ",
924+
"expected at least ", sstart,", got ", j-1)))
924925
end
925926
y = iterate(src, y[2])
926927
end
927928
if y === nothing
928-
throw(ArgumentError(string("source has fewer elements than required, ",
929-
"expected at least ",sstart,", got ",sstart-1)))
929+
throw(ArgumentError(LazyString(
930+
"source has fewer elements than required, ",
931+
"expected at least ",sstart," got ", sstart-1)))
930932
end
931933
i = Int(dstart)
932934
while y !== nothing
@@ -940,19 +942,22 @@ end
940942

941943
# this method must be separate from the above since src might not have a length
942944
function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer, n::Integer)
943-
n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative")))
945+
n < 0 && throw(ArgumentError(LazyString("tried to copy n=",n,
946+
", elements, but n should be nonnegative")))
944947
n == 0 && return dest
945948
dmax = dstart + n - 1
946949
inds = LinearIndices(dest)
947950
if (dstart inds || dmax inds) | (sstart < 1)
948-
sstart < 1 && throw(ArgumentError(string("source start offset (",sstart,") is < 1")))
951+
sstart < 1 && throw(ArgumentError(LazyString("source start offset (",
952+
sstart,") is < 1")))
949953
throw(BoundsError(dest, dstart:dmax))
950954
end
951955
y = iterate(src)
952956
for j = 1:(sstart-1)
953957
if y === nothing
954-
throw(ArgumentError(string("source has fewer elements than required, ",
955-
"expected at least ",sstart,", got ",j-1)))
958+
throw(ArgumentError(LazyString(
959+
"source has fewer elements than required, ",
960+
"expected at least ",sstart,", got ",j-1)))
956961
end
957962
y = iterate(src, y[2])
958963
end
@@ -1064,7 +1069,8 @@ function copyto!(dest::AbstractArray, dstart::Integer,
10641069
src::AbstractArray, sstart::Integer,
10651070
n::Integer)
10661071
n == 0 && return dest
1067-
n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative")))
1072+
n < 0 && throw(ArgumentError(LazyString("tried to copy n=",
1073+
n," elements, but n should be nonnegative")))
10681074
destinds, srcinds = LinearIndices(dest), LinearIndices(src)
10691075
(checkbounds(Bool, destinds, dstart) && checkbounds(Bool, destinds, dstart+n-1)) || throw(BoundsError(dest, dstart:dstart+n-1))
10701076
(checkbounds(Bool, srcinds, sstart) && checkbounds(Bool, srcinds, sstart+n-1)) || throw(BoundsError(src, sstart:sstart+n-1))
@@ -1082,12 +1088,12 @@ end
10821088
function copyto!(B::AbstractVecOrMat{R}, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int},
10831089
A::AbstractVecOrMat{S}, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) where {R,S}
10841090
if length(ir_dest) != length(ir_src)
1085-
throw(ArgumentError(string("source and destination must have same size (got ",
1086-
length(ir_src)," and ",length(ir_dest),")")))
1091+
throw(ArgumentError(LazyString("source and destination must have same size (got ",
1092+
length(ir_src)," and ",length(ir_dest),")")))
10871093
end
10881094
if length(jr_dest) != length(jr_src)
1089-
throw(ArgumentError(string("source and destination must have same size (got ",
1090-
length(jr_src)," and ",length(jr_dest),")")))
1095+
throw(ArgumentError(LazyString("source and destination must have same size (got ",
1096+
length(jr_src)," and ",length(jr_dest),")")))
10911097
end
10921098
@boundscheck checkbounds(B, ir_dest, jr_dest)
10931099
@boundscheck checkbounds(A, ir_src, jr_src)

base/compiler/compiler.jl

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ add_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_sadd_int(x, y)
6767
add_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_uadd_int(x, y)
6868
add_with_overflow(x::Bool, y::Bool) = (x+y, false)
6969

70+
include("strings/lazy.jl")
71+
7072
# core array operations
7173
include("indices.jl")
7274
include("array.jl")

base/exports.jl

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export
5757
IOStream,
5858
LinRange,
5959
Irrational,
60+
LazyString,
6061
Matrix,
6162
MergeSort,
6263
Missing,
@@ -985,6 +986,7 @@ export
985986
@v_str, # version number
986987
@raw_str, # raw string with no interpolation/unescaping
987988
@NamedTuple,
989+
@lazy_str, # lazy string
988990

989991
# documentation
990992
@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 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)