All files / yaml / _type / float.ts

100.00% Branches 58/58
100.00% Functions 4/4
100.00% Lines 81/81
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
 
 
 
 
 
 
x18
 
x18
 
x18
 
 
x18
 
x18
 
x18
 
 
x473
x473
x473
 
 
x473
x473
x422
x422
 
x51
x473
 
x51
x51
x51
 
x51
x4
x4
 
x51
x2
x2
x51
x1
x1
x48
x51
 
x18
 
x18
 
x18
x18
 
x18
x18
x3
x3
x1
x3
x1
x3
x1
x3
x18
x3
x3
x1
x3
x1
x3
x1
x3
x15
x3
x3
x1
x3
x1
x3
x1
x3
x12
x1
x1
 
x8
 
 
 
 
x18
x18
 
x322
x322
x322
x322
x322
 
x18
x18
x18
x18
x18
x18
x18
x18
x18














































































































// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2026 the Deno authors. MIT license.

import type { StyleVariant, Type } from "../_type.ts";
import { isNegativeZero } from "../_utils.ts";

const YAML_FLOAT_REGEXP = new RegExp(
  // 2.5e4, 2.5 and integers
  "^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?" +
    // .2e4, .2
    // special case, seems not from spec
    "|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?" +
    // .inf
    "|[-+]?\\.(?:inf|Inf|INF)" +
    // .nan
    "|\\.(?:nan|NaN|NAN))$",
);

function resolveYamlFloat(data: string): boolean {
  if (
    !YAML_FLOAT_REGEXP.test(data) ||
    // Quick hack to not allow integers end with `_`
    // Probably should update regexp & check speed
    data[data.length - 1] === "_"
  ) {
    return false;
  }

  return true;
}

function constructYamlFloat(data: string): number {
  let value = data.replace(/_/g, "").toLowerCase();
  const sign = value[0] === "-" ? -1 : 1;

  if (value[0] && "+-".includes(value[0])) {
    value = value.slice(1);
  }

  if (value === ".inf") {
    return sign === 1 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;
  }
  if (value === ".nan") {
    return NaN;
  }
  return sign * parseFloat(value);
}

const SCIENTIFIC_WITHOUT_DOT_REGEXP = /^[-+]?[0-9]+e/;

function representYamlFloat(
  // deno-lint-ignore ban-types
  object: number | Number,
  style?: StyleVariant,
): string {
  const value = object instanceof Number ? object.valueOf() : object;
  if (isNaN(value)) {
    switch (style) {
      case "lowercase":
        return ".nan";
      case "uppercase":
        return ".NAN";
      case "camelcase":
        return ".NaN";
    }
  } else if (Number.POSITIVE_INFINITY === value) {
    switch (style) {
      case "lowercase":
        return ".inf";
      case "uppercase":
        return ".INF";
      case "camelcase":
        return ".Inf";
    }
  } else if (Number.NEGATIVE_INFINITY === value) {
    switch (style) {
      case "lowercase":
        return "-.inf";
      case "uppercase":
        return "-.INF";
      case "camelcase":
        return "-.Inf";
    }
  } else if (isNegativeZero(value)) {
    return "-0.0";
  }

  const res = value.toString(10);

  // JS stringifier can build scientific format without dots: 5e-100,
  // while YAML requires dot: 5.e-100. Fix it with simple hack

  return SCIENTIFIC_WITHOUT_DOT_REGEXP.test(res) ? res.replace("e", ".e") : res;
}

function isFloat(object: unknown): object is number {
  if (object instanceof Number) object = object.valueOf();
  return typeof object === "number" &&
    (object % 1 !== 0 || isNegativeZero(object));
}

export const float: Type<"scalar", number> = {
  tag: "tag:yaml.org,2002:float",
  construct: constructYamlFloat,
  defaultStyle: "lowercase",
  kind: "scalar",
  predicate: isFloat,
  represent: representYamlFloat,
  resolve: resolveYamlFloat,
};