Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/schema/core/float.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const floatNaN: ScalarTag = {
: str[0] === '-'
? Number.NEGATIVE_INFINITY
: Number.POSITIVE_INFINITY,
stringify: stringifyNumber
stringify: node => stringifyNumber(node)
}

export const floatExp: ScalarTag = {
Expand Down Expand Up @@ -41,5 +41,5 @@ export const float: ScalarTag = {
node.minFractionDigits = str.length - dot - 1
return node
},
stringify: stringifyNumber
stringify: node => stringifyNumber(node)
}
2 changes: 1 addition & 1 deletion src/schema/core/int.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const int: ScalarTag = {
tag: 'tag:yaml.org,2002:int',
test: /^[-+]?[0-9]+$/,
resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt),
stringify: stringifyNumber
stringify: node => stringifyNumber(node)
}

export const intHex: ScalarTag = {
Expand Down
6 changes: 3 additions & 3 deletions src/schema/yaml-1.1/float.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const floatNaN: ScalarTag = {
: str[0] === '-'
? Number.NEGATIVE_INFINITY
: Number.POSITIVE_INFINITY,
stringify: stringifyNumber
stringify: node => stringifyNumber(node, '1.1')
}

export const floatExp: ScalarTag = {
Expand All @@ -25,7 +25,7 @@ export const floatExp: ScalarTag = {
resolve: (str: string) => parseFloat(str.replace(/_/g, '')),
stringify(node) {
const num = Number(node.value)
return isFinite(num) ? num.toExponential() : stringifyNumber(node)
return isFinite(num) ? num.toExponential() : stringifyNumber(node, '1.1')
}
}

Expand All @@ -43,5 +43,5 @@ export const float: ScalarTag = {
}
return node
},
stringify: stringifyNumber
stringify: node => stringifyNumber(node, '1.1')
}
2 changes: 1 addition & 1 deletion src/schema/yaml-1.1/int.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const int: ScalarTag = {
test: /^[-+]?[0-9][0-9_]*$/,
resolve: (str: string, _onError: unknown, opt: ParseOptions) =>
intResolve(str, 0, 10, opt),
stringify: stringifyNumber
stringify: node => stringifyNumber(node, '1.1')
}

export const intHex: ScalarTag = {
Expand Down
18 changes: 11 additions & 7 deletions src/stringify/stringifyNumber.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import type { Scalar } from '../nodes/Scalar.ts'

export function stringifyNumber({
format,
minFractionDigits,
tag,
value
}: Scalar): string {
export function stringifyNumber(
{ format, minFractionDigits, source, tag, value }: Scalar,
version: '1.1' | '1.2' = '1.2'
): string {
if (typeof value === 'bigint') return String(value)
const num = typeof value === 'number' ? value : Number(value)
if (!isFinite(num)) return isNaN(num) ? '.nan' : num < 0 ? '-.inf' : '.inf'
if (!isFinite(num)) {
if (typeof value === 'number' && source) {
const source_ = version === '1.1' ? source.replace(/_/g, '') : source
if (parseFloat(source_) === num) return source
}
return isNaN(num) ? '.nan' : num < 0 ? '-.inf' : '.inf'
}
let n = Object.is(value, -0) ? '-0' : JSON.stringify(value)
if (
!format &&
Expand Down
44 changes: 43 additions & 1 deletion tests/doc/types.ts
Comment thread
eemeli marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
YAMLMap,
YAMLSeq
} from 'yaml'
import { seqTag, stringifyString, stringTag } from 'yaml/util'
import { seqTag, stringifyNumber, stringifyString, stringTag } from 'yaml/util'
import { source } from '../_utils.ts'

const parseDocument = <T extends DocValue = DocValue>(
Expand Down Expand Up @@ -262,6 +262,48 @@ describe('number types', () => {
expect(doc.value[5]).not.toHaveProperty('minFractionDigits')
})
})

describe('overflowing values keep their source (#660)', () => {
test('float overflowing to Infinity round-trips via its source', () => {
expect(String(parseDocument('gitsha: 61e9540'))).toBe('gitsha: 61e9540\n')
})

test('negative overflow keeps its source', () => {
expect(String(parseDocument('n: -61e9540'))).toBe('n: -61e9540\n')
})

test('literal .inf / .nan are still emitted as such', () => {
expect(String(parseDocument('n: .inf'))).toBe('n: .inf\n')
expect(String(parseDocument('n: -.inf'))).toBe('n: -.inf\n')
expect(String(parseDocument('n: .nan'))).toBe('n: .nan\n')
})

test('a programmatic Infinity with finite source is emitted as .inf', () => {
const doc = parseDocument('n: 1')
doc.set('n', Infinity)
expect(String(doc)).toBe('n: .inf\n')
})

test('editing the value of a node parsed from a non-finite source is reflected', () => {
const doc = parseDocument('n: 61e9540')
const node = doc.get('n') as Scalar
node.value = -Infinity
expect(String(doc)).toBe('n: -.inf\n')
})

test('the source is used only while it still resolves to the value', () => {
const node = new Scalar(Infinity)
node.source = '61e9540'
expect(stringifyNumber(node)).toBe('61e9540')
node.source = '5'
expect(stringifyNumber(node)).toBe('.inf')
})

test('a YAML 1.1 float with underscores that overflows keeps its source', () => {
const doc = parseDocument('n: 6_1e9540', { version: '1.1' })
expect(String(doc)).toBe('n: 6_1e9540\n')
})
})
})

test('Indented sequence with sequence values (#2)', () => {
Expand Down
Loading