Skip to content

Commit

Permalink
Merge pull request #64 from JuliaString/spj/fixtypemin
Browse files Browse the repository at this point in the history
Fix issue 110 from Formatting.jl
  • Loading branch information
ScottPJones authored Jul 29, 2023
2 parents dffbbd7 + 52a25df commit 5f0fb70
Show file tree
Hide file tree
Showing 14 changed files with 856 additions and 144 deletions.
27 changes: 27 additions & 0 deletions .drone.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
kind: pipeline
name: linux - arm64 - Julia 1.9

platform:
os: linux
arch: arm64

steps:
- name: build
image: julia:1.9
commands:
- "julia --project=. --check-bounds=yes --color=yes -e 'using InteractiveUtils; versioninfo(verbose=true); using Pkg; Pkg.build(); Pkg.test(coverage=true)'"

---
kind: pipeline
name: linux - arm64 - Julia 1.9

platform:
os: linux
arch: arm64

steps:
- name: build
image: julia:1.9
commands:
- "julia --project=. --check-bounds=yes --color=yes -e 'using InteractiveUtils; versioninfo(verbose=true); using Pkg; Pkg.build(); Pkg.test(coverage=true)'"
15 changes: 15 additions & 0 deletions .github/workflows/TagBot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: TagBot
on:
issue_comment:
types:
- created
workflow_dispatch:
jobs:
TagBot:
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
runs-on: ubuntu-latest
steps:
- uses: JuliaRegistries/TagBot@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
ssh: ${{ secrets.DOCUMENTER_KEY }}
43 changes: 43 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: CI
on:
- push
- pull_request
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
version:
- '1.6'
- '1.9'
- 'nightly'
os:
- ubuntu-latest
- macOS-latest
- windows-latest
arch:
- x64
- x86
exclude:
- os: macOS-latest
arch: x86
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@v1
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: actions/cache@v1
env:
cache-name: cache-artifacts
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
23 changes: 0 additions & 23 deletions .travis.yml

This file was deleted.

10 changes: 5 additions & 5 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
authors = [
"ScottPJones <[email protected]>",
"Dahua Lin <[email protected]>",
"John M Kuhn <[email protected]>",
"ScottPJones <[email protected]>",
"Thomas Breloff <[email protected]>",
"Alex Arslan <[email protected]>",
"Chuan-Zheng Lee <[email protected]>",
Expand All @@ -23,17 +23,17 @@ keywords = ["Strings", "Formatting"]
license = "MIT"
name = "Format"
uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8"
version = "1.2.0"
version = "1.3.3"

[deps]
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[targets]
test = ["Test", "Random"]
test = ["Test", "Random", "Printf"]

[compat]
julia = "^1.0.0"
julia = "1.4"
2 changes: 0 additions & 2 deletions src/Format.jl
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,6 @@ module Format

import Base.show

using Printf

_stdout() = stdout
_codeunits(s) = Vector{UInt8}(codeunits(s))
m_eval(expr) = Core.eval(@__MODULE__, expr)
Expand Down
59 changes: 22 additions & 37 deletions src/cformat.jl
Original file line number Diff line number Diff line change
@@ -1,47 +1,32 @@
formatters = Dict{ ASCIIStr, Function }()
include("printf.jl")

cfmt( fmt::ASCIIStr, x ) = m_eval(Expr(:call, generate_formatter( fmt ), x))
const _formatters = Dict{ASCIIStr,FmtSpec}()

function checkfmt(fmt)
@static if VERSION > v"1.6.0-DEV.854"
test = Printf.Format(fmt)
length(test.formats) == 1 ||
error( "Only one AND undecorated format string is allowed")
else
test = @static VERSION >= v"1.4.0-DEV.180" ? Printf.parse(fmt) : Base.Printf.parse( fmt )
(length( test ) == 1 && typeof( test[1] ) <: Tuple) ||
error( "Only one AND undecorated format string is allowed")
end
end
function _get_formatter(fmt)
global _formatters

function generate_formatter( fmt::ASCIIStr )
global formatters
chkfmt = get(_formatters, fmt, nothing)
chkfmt === nothing || return chkfmt
_formatters[fmt] = FmtSpec(fmt)
end

haskey( formatters, fmt ) && return formatters[fmt]
_cfmt_comma(fspec::FmtSpec, x) = addcommasreal(_cfmt(fspec, x))
_cfmt_comma(fspec::FmtSpec{FmtStr}, x::Rational) = addcommasrat(_cfmt(fspec, x))
_cfmt_comma(fspec::FmtSpec{<:FmtInts}, x) = checkcommas(_cfmt(fspec, x))

if !occursin("'", fmt)
checkfmt(fmt)
formatter = @eval(x->@sprintf( $fmt, x ))
return (formatters[ fmt ] = x->Base.invokelatest(formatter, x))
end
function _cfmt(fspec::FmtSpec, x)
sv = Base.StringVector(23) # Trust that lower level code will expand if necessary
pos = _fmt(sv, 1, fspec, x)
resize!(sv, pos - 1)
String(sv)
end

conversion = fmt[end]
conversion in "sduifF" ||
error( string("thousand separator not defined for ", conversion, " conversion") )
cfmt(fspec::FmtSpec, x) = fspec.tsep == 0 ? _cfmt(fspec, x) : _cfmt_comma(fspec, x)
cfmt(fmtstr::ASCIIStr, x) = cfmt(_get_formatter(fmtstr), x)

fmtactual = replace( fmt, "'" => ""; count=1 )
checkfmt( fmtactual )
formatter =
if !(conversion in "sfF")
@eval(x->checkcommas(@sprintf( $fmtactual, x )))
elseif endswith( fmtactual, 's')
@eval((x::Real)->((eltype(x) <: Rational)
? addcommasrat(@sprintf( $fmtactual, x ))
: addcommasreal(@sprintf( $fmtactual, x ))))
else
@eval((x::Real)->addcommasreal(@sprintf( $fmtactual, x )))
end
return (formatters[ fmt ] = x->Base.invokelatest(formatter, x))
function generate_formatter(fmt::ASCIIStr)
fspec = _get_formatter(fmt)
fspec.tsep == 0 ? x -> _cfmt(fspec, x) : x -> _cfmt_comma(fspec, x)
end

function addcommasreal(s)
Expand Down
90 changes: 67 additions & 23 deletions src/fmtcore.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,12 @@ function _pfmt_s(out::IO, fs::FormatSpec, s::Union{AbstractString,AbstractChar})
slen = length(s)
if wid <= slen
print(out, s)
elseif fs.align == '<'
print(out, s)
_repprint(out, fs.fill, wid-slen)
else
a = fs.align
if a == '<'
print(out, s)
_repprint(out, fs.fill, wid-slen)
else
_repprint(out, fs.fill, wid-slen)
print(out, s)
end
_repprint(out, fs.fill, wid-slen)
print(out, s)
end
end

Expand All @@ -44,6 +41,11 @@ _div(x::Integer, ::_Bin) = x >> 1
_div(x::Integer, ::_Oct) = x >> 3
_div(x::Integer, ::Union{_Hex, _HEX}) = x >> 4

_str(x::Integer, ::_Dec) = string(x, base=10)
_str(x::Integer, ::_Bin) = string(x, base=2)
_str(x::Integer, ::_Oct) = string(x, base=8)
_str(x::Integer, ::Union{_Hex, _HEX}) = string(x, base=16)

function _ndigits(x::Integer, op) # suppose x is non-negative
m = 1
q = _div(x, op)
Expand Down Expand Up @@ -96,15 +98,53 @@ function _pfmt_intdigits(out::IO, ax::T, op::Op) where {Op, T<:Integer}
end
end

function _pfmt_intmin(out::IO, ip::ASCIIStr, zs::Integer, s::String)
# print sign
print(out, '-')
# print prefix
isempty(ip) || print(out, ip)
# print padding zeros
zs > 0 && _repprint(out, '0', zs)
# print actual digits
print(out, SubString(s, 2))
nothing
end

# Special case were abs would give error
function _pfmt_imin(out::IO, fs::FormatSpec, x::Integer, op::Op) where {Op}
s = _str(x, op)
xlen = length(s)
# prefix (e.g. 0x, 0b, 0o)
ip = ""
if fs.ipre
ip = _ipre(op)
xlen += length(ip)
end

# printing
wid = fs.width
if wid <= xlen
_pfmt_intmin(out, ip, 0, s)
elseif fs.zpad
_pfmt_intmin(out, ip, wid-xlen, s)
elseif fs.align == '<'
_pfmt_intmin(out, ip, 0, s)
_repprint(out, fs.fill, wid-xlen)
else
_repprint(out, fs.fill, wid-xlen)
_pfmt_intmin(out, ip, 0, s)
end
end

function _pfmt_i(out::IO, fs::FormatSpec, x::Integer, op::Op) where {Op}
# Specially handle edge case of typemin
x === typemin(typeof(x)) && x isa Signed && return _pfmt_imin(out, fs, x, op)
# calculate actual length
ax = abs(x)
xlen = _ndigits(abs(x), op)
xlen = _ndigits(ax, op)
# sign char
sch = _signchar(x, fs.sign)
if sch != '\0'
xlen += 1
end
xlen += (sch != '\0')
# prefix (e.g. 0x, 0b, 0o)
ip = ""
if fs.ipre
Expand All @@ -118,15 +158,12 @@ function _pfmt_i(out::IO, fs::FormatSpec, x::Integer, op::Op) where {Op}
_pfmt_int(out, sch, ip, 0, ax, op)
elseif fs.zpad
_pfmt_int(out, sch, ip, wid-xlen, ax, op)
elseif fs.align == '<'
_pfmt_int(out, sch, ip, 0, ax, op)
_repprint(out, fs.fill, wid-xlen)
else
a = fs.align
if a == '<'
_pfmt_int(out, sch, ip, 0, ax, op)
_repprint(out, fs.fill, wid-xlen)
else
_repprint(out, fs.fill, wid-xlen)
_pfmt_int(out, sch, ip, 0, ax, op)
end
_repprint(out, fs.fill, wid-xlen)
_pfmt_int(out, sch, ip, 0, ax, op)
end
end

Expand All @@ -147,10 +184,9 @@ function _pfmt_float(out::IO, sch::AbstractChar, zs::Integer, intv::Real, decv::
else
_pfmt_intdigits(out, intv, _Dec())
end
# print decimal point
print(out, '.')
# print decimal part
if prec > 0
print(out, '.')
nd = _ndigits(idecv, _Dec())
nd < prec && _repprint(out, '0', prec - nd)
_pfmt_intdigits(out, idecv, _Dec())
Expand All @@ -165,7 +201,7 @@ function _pfmt_f(out::IO, fs::FormatSpec, x::AbstractFloat)
decv = rax - intv

# calculate length
xlen = _ndigits(intv, _Dec()) + 1 + fs.prec
xlen = _ndigits(intv, _Dec()) + ifelse(fs.prec > 0, fs.prec + 1, 0)
sch != '\0' && (xlen += 1)

# print
Expand Down Expand Up @@ -217,6 +253,14 @@ function _pfmt_e(out::IO, fs::FormatSpec, x::AbstractFloat)
rax = round(ax; sigdigits = fs.prec + 1)
e = floor(Integer, log10(rax)) # exponent
u = rax * exp10(-e) # significand
i = 0
v10 = 1
while isinf(u)
i += 1
i > 18 && (u = 0.0; e = 0; break)
v10 *= 10
u = v10 * rax * exp(-e - i)
end
end

# calculate length
Expand Down
1 change: 1 addition & 0 deletions src/fmtspec.jl
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ mutable struct _Bin end

_srepr(x) = repr(x)
_srepr(x::AbstractString) = x
_srepr(x::Symbol) = string(x)
_srepr(x::AbstractChar) = string(x)
_srepr(x::Enum) = string(x)

Expand Down
Loading

0 comments on commit 5f0fb70

Please sign in to comment.