typia 14.0.6 → 15.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/lib/http.d.ts +49 -24
- package/lib/http.js.map +1 -1
- package/lib/http.mjs.map +1 -1
- package/lib/internal/_createLlmEvaluation.d.ts +1 -0
- package/lib/internal/_createLlmEvaluation.js +486 -0
- package/lib/internal/_createLlmEvaluation.js.map +1 -0
- package/lib/internal/_createLlmEvaluation.mjs +382 -0
- package/lib/internal/_createLlmEvaluation.mjs.map +1 -0
- package/lib/internal/_httpFormDataReadBigint.js +7 -5
- package/lib/internal/_httpFormDataReadBigint.js.map +1 -1
- package/lib/internal/_httpFormDataReadBigint.mjs +1 -1
- package/lib/internal/_httpFormDataReadBigint.mjs.map +1 -1
- package/lib/internal/_httpFormDataReadBoolean.js +12 -10
- package/lib/internal/_httpFormDataReadBoolean.js.map +1 -1
- package/lib/internal/_httpFormDataReadBoolean.mjs +1 -1
- package/lib/internal/_httpFormDataReadBoolean.mjs.map +1 -1
- package/lib/internal/_httpFormDataReadNumber.js +7 -5
- package/lib/internal/_httpFormDataReadNumber.js.map +1 -1
- package/lib/internal/_httpFormDataReadNumber.mjs +1 -1
- package/lib/internal/_httpFormDataReadNumber.mjs.map +1 -1
- package/lib/internal/_httpFormDataReadString.d.ts +9 -1
- package/lib/internal/_httpFormDataReadString.js +10 -2
- package/lib/internal/_httpFormDataReadString.js.map +1 -1
- package/lib/internal/_httpFormDataReadString.mjs +9 -1
- package/lib/internal/_httpFormDataReadString.mjs.map +1 -1
- package/lib/internal/_httpHeaderReadBigint.js +5 -1
- package/lib/internal/_httpHeaderReadBigint.js.map +1 -1
- package/lib/internal/_httpHeaderReadBigint.mjs +2 -1
- package/lib/internal/_httpHeaderReadBigint.mjs.map +1 -1
- package/lib/internal/_httpHeaderReadNumber.js +5 -1
- package/lib/internal/_httpHeaderReadNumber.js.map +1 -1
- package/lib/internal/_httpHeaderReadNumber.mjs +2 -1
- package/lib/internal/_httpHeaderReadNumber.mjs.map +1 -1
- package/lib/internal/_httpParameterReadBigint.js +4 -0
- package/lib/internal/_httpParameterReadBigint.js.map +1 -1
- package/lib/internal/_httpParameterReadBigint.mjs +1 -0
- package/lib/internal/_httpParameterReadBigint.mjs.map +1 -1
- package/lib/internal/_httpParameterReadNumber.js +4 -0
- package/lib/internal/_httpParameterReadNumber.js.map +1 -1
- package/lib/internal/_httpParameterReadNumber.mjs +1 -0
- package/lib/internal/_httpParameterReadNumber.mjs.map +1 -1
- package/lib/internal/_httpParameterReadString.d.ts +9 -1
- package/lib/internal/_httpParameterReadString.js +9 -1
- package/lib/internal/_httpParameterReadString.js.map +1 -1
- package/lib/internal/_httpParameterReadString.mjs +9 -1
- package/lib/internal/_httpParameterReadString.mjs.map +1 -1
- package/lib/internal/_httpQueryReadBigint.js +8 -1
- package/lib/internal/_httpQueryReadBigint.js.map +1 -1
- package/lib/internal/_httpQueryReadBigint.mjs +1 -1
- package/lib/internal/_httpQueryReadBigint.mjs.map +1 -1
- package/lib/internal/_httpQueryReadBoolean.js +4 -1
- package/lib/internal/_httpQueryReadBoolean.js.map +1 -1
- package/lib/internal/_httpQueryReadBoolean.mjs +1 -1
- package/lib/internal/_httpQueryReadBoolean.mjs.map +1 -1
- package/lib/internal/_httpQueryReadNumber.js +8 -1
- package/lib/internal/_httpQueryReadNumber.js.map +1 -1
- package/lib/internal/_httpQueryReadNumber.mjs +1 -1
- package/lib/internal/_httpQueryReadNumber.mjs.map +1 -1
- package/lib/internal/_httpQueryReadString.d.ts +9 -1
- package/lib/internal/_httpQueryReadString.js +9 -1
- package/lib/internal/_httpQueryReadString.js.map +1 -1
- package/lib/internal/_httpQueryReadString.mjs +9 -1
- package/lib/internal/_httpQueryReadString.mjs.map +1 -1
- package/lib/internal/_randomBigint.d.ts +12 -1
- package/lib/internal/_randomBigint.js +90 -2
- package/lib/internal/_randomBigint.js.map +1 -1
- package/lib/internal/_randomBigint.mjs +70 -2
- package/lib/internal/_randomBigint.mjs.map +1 -1
- package/lib/internal/_randomInteger.js +21 -12
- package/lib/internal/_randomInteger.js.map +1 -1
- package/lib/internal/_randomInteger.mjs +21 -16
- package/lib/internal/_randomInteger.mjs.map +1 -1
- package/lib/internal/_randomNumber.js +19 -10
- package/lib/internal/_randomNumber.js.map +1 -1
- package/lib/internal/_randomNumber.mjs +19 -14
- package/lib/internal/_randomNumber.mjs.map +1 -1
- package/lib/internal/_stringLengthGte.d.ts +1 -0
- package/lib/internal/_stringLengthGte.js +14 -0
- package/lib/internal/_stringLengthGte.js.map +1 -0
- package/lib/internal/_stringLengthGte.mjs +11 -0
- package/lib/internal/_stringLengthGte.mjs.map +1 -0
- package/lib/internal/_stringLengthLte.d.ts +1 -0
- package/lib/internal/_stringLengthLte.js +14 -0
- package/lib/internal/_stringLengthLte.js.map +1 -0
- package/lib/internal/_stringLengthLte.mjs +11 -0
- package/lib/internal/_stringLengthLte.mjs.map +1 -0
- package/lib/llm.d.ts +54 -1
- package/lib/llm.js +5 -0
- package/lib/llm.js.map +1 -1
- package/lib/llm.mjs +6 -1
- package/lib/llm.mjs.map +1 -1
- package/lib/re-exports.d.ts +1 -1
- package/lib/re-exports.js.map +1 -1
- package/native/cmd/ttsc-typia/build.go +2 -0
- package/native/cmd/ttsc-typia/dynamic_key_tags_transform_test.go +1 -1
- package/native/cmd/ttsc-typia/llm_evaluation_accepts_property_probability_test.go +27 -0
- package/native/cmd/ttsc-typia/llm_evaluation_accepts_unselected_source_probability_test.go +26 -0
- package/native/cmd/ttsc-typia/llm_evaluation_complete_object_shape_test.go +96 -0
- package/native/cmd/ttsc-typia/llm_evaluation_ignores_declaration_probability_without_call_test.go +18 -0
- package/native/cmd/ttsc-typia/llm_evaluation_plan_emission_transform_test.go +90 -0
- package/native/cmd/ttsc-typia/llm_evaluation_program_scan_dependencies_test.go +69 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_alias_comment_test.go +44 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_alias_refinement_test.go +45 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_computed_method_probability_test.go +30 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_imported_declaration_probability_test.go +48 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_invalid_probability_test.go +208 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_refinements_test.go +94 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_undecidable_types_test.go +232 -0
- package/native/cmd/ttsc-typia/metadata_type_tag_sole_malformed_diagnostic_test.go +94 -0
- package/native/cmd/ttsc-typia/random_recursive_min_items_diagnostic_test.go +18 -1
- package/native/cmd/ttsc-typia/template_literal_type_tags_transform_test.go +3 -2
- package/native/cmd/ttsc-typia/transform.go +2 -0
- package/native/cmd/ttsc-typia/transform_helpers_test.go +17 -3
- package/native/core/context/ITypiaContext.go +4 -0
- package/native/core/factories/ExpressionFactory.go +16 -1
- package/native/core/factories/IdentifierFactory.go +10 -0
- package/native/core/factories/LiteralFactory.go +16 -0
- package/native/core/factories/MetadataCommentTagFactory.go +185 -35
- package/native/core/factories/MetadataTypeTagFactory.go +97 -24
- package/native/core/factories/MetadataTypeTagSchemaFactory.go +5 -0
- package/native/core/factories/expression_factory_number_spells_non_finite_values_test.go +69 -0
- package/native/core/factories/internal/metadata/IMetadataIteratorProps.go +6 -2
- package/native/core/factories/internal/metadata/emplace_metadata_object.go +49 -15
- package/native/core/factories/literal_factory_null_survives_objects_test.go +43 -0
- package/native/core/factories/metadata_comment_tag_factory_holds_bigint_tags_exact_test.go +168 -0
- package/native/core/factories/metadata_comment_tag_factory_reads_javascript_numbers_test.go +158 -0
- package/native/core/factories/metadata_comment_tag_factory_string_length_helpers_test.go +55 -0
- package/native/core/factories/metadata_type_tag_factory_reports_all_invalid_candidates_test.go +73 -0
- package/native/core/programmers/functional/FunctionalAssertParametersProgrammer.go +1 -3
- package/native/core/programmers/functional/FunctionalIsParametersProgrammer.go +107 -9
- package/native/core/programmers/functional/FunctionalValidateParametersProgrammer.go +1 -3
- package/native/core/programmers/http/HttpFormDataProgrammer.go +41 -2
- package/native/core/programmers/http/HttpHeadersProgrammer.go +56 -4
- package/native/core/programmers/http/HttpParameterProgrammer.go +1 -3
- package/native/core/programmers/http/HttpQueryProgrammer.go +26 -43
- package/native/core/programmers/iterate/json_schema_jsDocTags.go +10 -3
- package/native/core/programmers/iterate/json_schema_jsdoc_tags_cast_reads_javascript_numbers_test.go +64 -0
- package/native/core/programmers/llm/LlmApplicationProgrammer.go +1 -0
- package/native/core/programmers/llm/LlmEvaluationProgrammer.go +559 -0
- package/native/core/programmers/llm/LlmParametersProgrammer.go +6 -5
- package/native/core/programmers/llm/LlmReferenceDescription.go +113 -0
- package/native/core/programmers/llm/LlmSchemaProgrammer.go +34 -4
- package/native/core/programmers/llm/llm_reference_description_test.go +134 -0
- package/native/core/schemas/metadata/MetadataDependency.go +9 -0
- package/native/core/utils/NumberUtil.go +195 -0
- package/native/core/utils/number_util_rational_keeps_written_value_test.go +62 -0
- package/native/core/utils/number_util_reads_javascript_number_grammar_test.go +147 -0
- package/native/transform/CallExpressionTransformer.go +1 -0
- package/native/transform/FileTransformer.go +5 -0
- package/native/transform/features/llm/LlmEvaluationProbabilityDeclarations.go +94 -0
- package/native/transform/features/llm/LlmEvaluationProbabilityDeclarations_test.go +50 -0
- package/native/transform/features/llm/LlmEvaluationTransformer.go +59 -0
- package/native/transform/features/llm/LlmSchemaTransformer.go +14 -11
- package/native/transform/features/llm/llm_transformer_entry_coverage_test.go +29 -27
- package/package.json +3 -3
- package/src/http.ts +61 -34
- package/src/internal/_createLlmEvaluation.ts +678 -0
- package/src/internal/_httpFormDataReadBigint.ts +3 -1
- package/src/internal/_httpFormDataReadBoolean.ts +3 -1
- package/src/internal/_httpFormDataReadNumber.ts +3 -1
- package/src/internal/_httpFormDataReadString.ts +10 -1
- package/src/internal/_httpHeaderReadBigint.ts +6 -1
- package/src/internal/_httpHeaderReadNumber.ts +6 -1
- package/src/internal/_httpParameterReadBigint.ts +3 -0
- package/src/internal/_httpParameterReadNumber.ts +3 -0
- package/src/internal/_httpParameterReadString.ts +12 -2
- package/src/internal/_httpQueryReadBigint.ts +7 -1
- package/src/internal/_httpQueryReadBoolean.ts +3 -1
- package/src/internal/_httpQueryReadNumber.ts +7 -1
- package/src/internal/_httpQueryReadString.ts +10 -1
- package/src/internal/_randomBigint.ts +111 -3
- package/src/internal/_randomInteger.ts +31 -16
- package/src/internal/_randomNumber.ts +23 -12
- package/src/internal/_stringLengthGte.ts +6 -0
- package/src/internal/_stringLengthLte.ts +6 -0
- package/src/llm.ts +64 -0
- package/src/re-exports.ts +1 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
package llm
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
|
|
6
|
+
nativeiterate "github.com/samchon/typia/packages/typia/native/core/programmers/iterate"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// llmReferenceDescription_root describes a parameters root that was reached
|
|
10
|
+
// through `$ref`, the way `LlmSchemaConverter.parameters` does with
|
|
11
|
+
// `JsonDescriptor.cascade({ escape: true })`: the type's own description, then
|
|
12
|
+
// each described namespace parent quoted, or `Current Type: {@link Name}` when
|
|
13
|
+
// the type carries none (samchon/typia#2405). A root that is not a reference
|
|
14
|
+
// keeps `own`.
|
|
15
|
+
func llmReferenceDescription_root(
|
|
16
|
+
schema nativeiterate.JsonSchema,
|
|
17
|
+
components *nativeiterate.OpenApi_IComponents,
|
|
18
|
+
own *string,
|
|
19
|
+
) *string {
|
|
20
|
+
ref, ok := schema["$ref"].(string)
|
|
21
|
+
if ok == false {
|
|
22
|
+
return own
|
|
23
|
+
}
|
|
24
|
+
key, ok := llmReferenceDescription_key(ref)
|
|
25
|
+
if ok == false {
|
|
26
|
+
return own
|
|
27
|
+
}
|
|
28
|
+
return llmReferenceDescription_cascade(key, components, own)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// llmReferenceDescription_cascade ports `JsonDescriptor.cascade` with
|
|
32
|
+
// `escape: true`.
|
|
33
|
+
func llmReferenceDescription_cascade(
|
|
34
|
+
key string,
|
|
35
|
+
components *nativeiterate.OpenApi_IComponents,
|
|
36
|
+
own *string,
|
|
37
|
+
) *string {
|
|
38
|
+
type parent struct {
|
|
39
|
+
key string
|
|
40
|
+
description *string
|
|
41
|
+
}
|
|
42
|
+
accessors := strings.Split(key, ".")
|
|
43
|
+
parents := []parent{}
|
|
44
|
+
for i := len(accessors); i >= 1; i-- {
|
|
45
|
+
current := strings.Join(accessors[:i], ".")
|
|
46
|
+
var description *string
|
|
47
|
+
if components != nil && components.Schemas != nil {
|
|
48
|
+
if target, found := components.Schemas[current]; found {
|
|
49
|
+
if text, ok := target["description"].(string); ok {
|
|
50
|
+
description = &text
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// the type itself always stays; a parent only when it is described
|
|
55
|
+
if i == len(accessors) || (description != nil && *description != "") {
|
|
56
|
+
parents = append(parents, parent{key: current, description: description})
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (own == nil || *own == "") && len(parents) == 0 {
|
|
60
|
+
return nil
|
|
61
|
+
}
|
|
62
|
+
parts := []string{}
|
|
63
|
+
if own != nil && *own != "" {
|
|
64
|
+
parts = append(parts, *own)
|
|
65
|
+
}
|
|
66
|
+
for i, p := range parents {
|
|
67
|
+
if p.description == nil {
|
|
68
|
+
parts = append(parts, "Current Type: {@link "+p.key+"}")
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
role := "parent"
|
|
72
|
+
if i == 0 {
|
|
73
|
+
role = "current"
|
|
74
|
+
}
|
|
75
|
+
lines := strings.Split(*p.description, "\n")
|
|
76
|
+
for j, line := range lines {
|
|
77
|
+
lines[j] = "> " + line
|
|
78
|
+
}
|
|
79
|
+
parts = append(parts, "Description of the "+role+" {@link "+p.key+"} type:\n\n"+strings.Join(lines, "\n"))
|
|
80
|
+
}
|
|
81
|
+
output := strings.Join(parts, "\n\n------------------------------\n\n")
|
|
82
|
+
return &output
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// llmReferenceDescription_key reads the component key of a
|
|
86
|
+
// `#/components/schemas/` reference. Native references carry the raw key, the
|
|
87
|
+
// one `components.schemas` is indexed by; `LlmReference.readOpenApi` decodes
|
|
88
|
+
// the escaped form of the same key in an emitted document.
|
|
89
|
+
func llmReferenceDescription_key(ref string) (string, bool) {
|
|
90
|
+
const prefix = "#/components/schemas/"
|
|
91
|
+
if strings.HasPrefix(ref, prefix) == false {
|
|
92
|
+
return "", false
|
|
93
|
+
}
|
|
94
|
+
return strings.TrimPrefix(ref, prefix), true
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// llmReferenceDescription_assign sets the cascaded root description on a
|
|
98
|
+
// converted parameters object, removing it when the cascade yields none.
|
|
99
|
+
func llmReferenceDescription_assign(
|
|
100
|
+
output map[string]any,
|
|
101
|
+
schema nativeiterate.JsonSchema,
|
|
102
|
+
components *nativeiterate.OpenApi_IComponents,
|
|
103
|
+
) {
|
|
104
|
+
var own *string
|
|
105
|
+
if text, ok := output["description"].(string); ok {
|
|
106
|
+
own = &text
|
|
107
|
+
}
|
|
108
|
+
if description := llmReferenceDescription_root(schema, components, own); description != nil {
|
|
109
|
+
output["description"] = *description
|
|
110
|
+
} else {
|
|
111
|
+
delete(output, "description")
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -15,6 +15,7 @@ import (
|
|
|
15
15
|
nativeiterate "github.com/samchon/typia/packages/typia/native/core/programmers/iterate"
|
|
16
16
|
nativejson "github.com/samchon/typia/packages/typia/native/core/programmers/json"
|
|
17
17
|
schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
|
|
18
|
+
nativeutils "github.com/samchon/typia/packages/typia/native/core/utils"
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
type llmSchemaProgrammerNamespace struct{}
|
|
@@ -709,11 +710,11 @@ func llmSchemaProgrammer_shift_array(schema map[string]any) map[string]any {
|
|
|
709
710
|
output := llmSchemaProgrammer_clone(schema)
|
|
710
711
|
tags := []string{}
|
|
711
712
|
if value, ok := output["minItems"]; ok {
|
|
712
|
-
tags = append(tags, "@minItems "+
|
|
713
|
+
tags = append(tags, "@minItems "+llmSchemaProgrammer_tag_text(value))
|
|
713
714
|
delete(output, "minItems")
|
|
714
715
|
}
|
|
715
716
|
if value, ok := output["maxItems"]; ok {
|
|
716
|
-
tags = append(tags, "@maxItems "+
|
|
717
|
+
tags = append(tags, "@maxItems "+llmSchemaProgrammer_tag_text(value))
|
|
717
718
|
delete(output, "maxItems")
|
|
718
719
|
}
|
|
719
720
|
if value, ok := output["uniqueItems"].(bool); ok {
|
|
@@ -732,7 +733,7 @@ func llmSchemaProgrammer_shift_numeric(schema map[string]any) map[string]any {
|
|
|
732
733
|
tags := []string{}
|
|
733
734
|
for _, key := range []string{"minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf", "default"} {
|
|
734
735
|
if value, ok := output[key]; ok {
|
|
735
|
-
tags = append(tags, "@"+key+" "+
|
|
736
|
+
tags = append(tags, "@"+key+" "+llmSchemaProgrammer_tag_text(value))
|
|
736
737
|
delete(output, key)
|
|
737
738
|
}
|
|
738
739
|
}
|
|
@@ -745,7 +746,7 @@ func llmSchemaProgrammer_shift_string(schema map[string]any) map[string]any {
|
|
|
745
746
|
tags := []string{}
|
|
746
747
|
for _, key := range []string{"minLength", "maxLength", "format", "pattern", "contentMediaType", "default"} {
|
|
747
748
|
if value, ok := output[key]; ok {
|
|
748
|
-
tags = append(tags, "@"+key+" "+
|
|
749
|
+
tags = append(tags, "@"+key+" "+llmSchemaProgrammer_tag_text(value))
|
|
749
750
|
delete(output, key)
|
|
750
751
|
}
|
|
751
752
|
}
|
|
@@ -753,6 +754,35 @@ func llmSchemaProgrammer_shift_string(schema map[string]any) map[string]any {
|
|
|
753
754
|
return output
|
|
754
755
|
}
|
|
755
756
|
|
|
757
|
+
// llmSchemaProgrammer_tag_text writes a shifted constraint value as `@typia/utils`'
|
|
758
|
+
// OpenApiConstraintShifter does, through JavaScript's template interpolation.
|
|
759
|
+
//
|
|
760
|
+
// A numeric literal from a type tag arrives as the compiler's `jsnum.Number`,
|
|
761
|
+
// whose `String()` already follows JavaScript, but a comment tag's value is a
|
|
762
|
+
// plain float64, and `fmt.Sprint` spells it the Go way: `1e+06` for 1000000,
|
|
763
|
+
// `1e-07` for 1e-7, `+Inf` for an infinity (samchon/typia#2452). Every float is
|
|
764
|
+
// therefore spelled by `NumberUtil.String`, whatever its defined type, and a
|
|
765
|
+
// pointer is followed to the value it holds.
|
|
766
|
+
func llmSchemaProgrammer_tag_text(value any) string {
|
|
767
|
+
reflected := reflect.ValueOf(value)
|
|
768
|
+
for reflected.IsValid() && reflected.Kind() == reflect.Pointer && reflected.IsNil() == false {
|
|
769
|
+
reflected = reflected.Elem()
|
|
770
|
+
}
|
|
771
|
+
if reflected.IsValid() == false {
|
|
772
|
+
return fmt.Sprint(value)
|
|
773
|
+
}
|
|
774
|
+
switch reflected.Kind() {
|
|
775
|
+
case reflect.Float32, reflect.Float64:
|
|
776
|
+
return nativeutils.NumberUtil.String(reflected.Float())
|
|
777
|
+
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
778
|
+
// JavaScript holds the count as a double, rounding past 2^53.
|
|
779
|
+
return nativeutils.NumberUtil.String(float64(reflected.Int()))
|
|
780
|
+
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
781
|
+
return nativeutils.NumberUtil.String(float64(reflected.Uint()))
|
|
782
|
+
}
|
|
783
|
+
return fmt.Sprint(reflected.Interface())
|
|
784
|
+
}
|
|
785
|
+
|
|
756
786
|
func llmSchemaProgrammer_write_tag_with_description(schema map[string]any, tags []string) {
|
|
757
787
|
if len(tags) == 0 {
|
|
758
788
|
return
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
package llm
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"testing"
|
|
5
|
+
|
|
6
|
+
nativeiterate "github.com/samchon/typia/packages/typia/native/core/programmers/iterate"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestLlmReferenceDescription locks the root description of a parameters
|
|
10
|
+
// object reached through `$ref` to `JsonDescriptor.cascade({ escape: true })`,
|
|
11
|
+
// the description `LlmSchemaConverter.parameters` writes for the same
|
|
12
|
+
// collection (samchon/typia#2405).
|
|
13
|
+
func TestLlmReferenceDescription(t *testing.T) {
|
|
14
|
+
t.Parallel()
|
|
15
|
+
const separator = "\n\n------------------------------\n\n"
|
|
16
|
+
text := func(value string) *string { return &value }
|
|
17
|
+
components := &nativeiterate.OpenApi_IComponents{
|
|
18
|
+
Schemas: map[string]nativeiterate.JsonSchema{
|
|
19
|
+
"IPlain": {"type": "object"},
|
|
20
|
+
"IMember": {"type": "object", "description": "Member.\nSecond line."},
|
|
21
|
+
"IMember.ICreate": {"type": "object", "description": "Creation."},
|
|
22
|
+
"IMember.IBare": {"type": "object"},
|
|
23
|
+
"IMember.Deep": {"type": "object"},
|
|
24
|
+
"IMember.Deep.ILeaf": {"type": "object"},
|
|
25
|
+
"IMember.IEmpty": {"type": "object", "description": ""},
|
|
26
|
+
"INamespaceless.IItem": {"type": "object"},
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
cases := []struct {
|
|
30
|
+
name string
|
|
31
|
+
schema nativeiterate.JsonSchema
|
|
32
|
+
own *string
|
|
33
|
+
expected *string
|
|
34
|
+
}{
|
|
35
|
+
{
|
|
36
|
+
name: "undescribed",
|
|
37
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IPlain"},
|
|
38
|
+
expected: text("Current Type: {@link IPlain}"),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "described",
|
|
42
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember"},
|
|
43
|
+
own: text("Member.\nSecond line."),
|
|
44
|
+
expected: text("Member.\nSecond line." + separator +
|
|
45
|
+
"Description of the current {@link IMember} type:\n\n> Member.\n> Second line."),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "described child of a described parent",
|
|
49
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember.ICreate"},
|
|
50
|
+
own: text("Creation."),
|
|
51
|
+
expected: text("Creation." + separator +
|
|
52
|
+
"Description of the current {@link IMember.ICreate} type:\n\n> Creation." + separator +
|
|
53
|
+
"Description of the parent {@link IMember} type:\n\n> Member.\n> Second line."),
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "undescribed child of a described parent",
|
|
57
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember.IBare"},
|
|
58
|
+
expected: text("Current Type: {@link IMember.IBare}" + separator +
|
|
59
|
+
"Description of the parent {@link IMember} type:\n\n> Member.\n> Second line."),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "undescribed middle parent is skipped",
|
|
63
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember.Deep.ILeaf"},
|
|
64
|
+
expected: text("Current Type: {@link IMember.Deep.ILeaf}" + separator +
|
|
65
|
+
"Description of the parent {@link IMember} type:\n\n> Member.\n> Second line."),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "empty own description renders quoted",
|
|
69
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember.IEmpty"},
|
|
70
|
+
own: text(""),
|
|
71
|
+
expected: text("Description of the current {@link IMember.IEmpty} type:\n\n> " + separator +
|
|
72
|
+
"Description of the parent {@link IMember} type:\n\n> Member.\n> Second line."),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "parent missing from components",
|
|
76
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/INamespaceless.IItem"},
|
|
77
|
+
expected: text("Current Type: {@link INamespaceless.IItem}"),
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "inline object keeps its own description",
|
|
81
|
+
schema: nativeiterate.JsonSchema{"type": "object"},
|
|
82
|
+
own: text("Inline."),
|
|
83
|
+
expected: text("Inline."),
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "inline object without description",
|
|
87
|
+
schema: nativeiterate.JsonSchema{"type": "object"},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "foreign reference keeps its own description",
|
|
91
|
+
schema: nativeiterate.JsonSchema{"$ref": "#/$defs/IPlain"},
|
|
92
|
+
own: text("Own."),
|
|
93
|
+
expected: text("Own."),
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
for _, c := range cases {
|
|
97
|
+
actual := llmReferenceDescription_root(c.schema, components, c.own)
|
|
98
|
+
if (actual == nil) != (c.expected == nil) || (actual != nil && *actual != *c.expected) {
|
|
99
|
+
t.Errorf("%s:\nexpected %s\nactual %s", c.name, llmReferenceDescriptionTest_show(c.expected), llmReferenceDescriptionTest_show(actual))
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// TestLlmReferenceDescriptionAssign locks that the parameters object loses a
|
|
105
|
+
// stale description when the cascade yields none, and gains the cascade
|
|
106
|
+
// otherwise.
|
|
107
|
+
func TestLlmReferenceDescriptionAssign(t *testing.T) {
|
|
108
|
+
t.Parallel()
|
|
109
|
+
components := &nativeiterate.OpenApi_IComponents{
|
|
110
|
+
Schemas: map[string]nativeiterate.JsonSchema{"IPlain": {"type": "object"}},
|
|
111
|
+
}
|
|
112
|
+
output := map[string]any{"type": "object"}
|
|
113
|
+
llmReferenceDescription_assign(output, nativeiterate.JsonSchema{"$ref": "#/components/schemas/IPlain"}, components)
|
|
114
|
+
if output["description"] != "Current Type: {@link IPlain}" {
|
|
115
|
+
t.Fatalf("reference root: got %#v", output["description"])
|
|
116
|
+
}
|
|
117
|
+
inline := map[string]any{"type": "object", "description": ""}
|
|
118
|
+
llmReferenceDescription_assign(inline, nativeiterate.JsonSchema{"type": "object"}, components)
|
|
119
|
+
if value, ok := inline["description"]; ok == false || value != "" {
|
|
120
|
+
t.Fatalf("inline root must keep its own description: got %#v", inline)
|
|
121
|
+
}
|
|
122
|
+
bare := map[string]any{"type": "object"}
|
|
123
|
+
llmReferenceDescription_assign(bare, nativeiterate.JsonSchema{"type": "object"}, components)
|
|
124
|
+
if _, ok := bare["description"]; ok {
|
|
125
|
+
t.Fatalf("inline root without description must stay undescribed: got %#v", bare)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
func llmReferenceDescriptionTest_show(value *string) string {
|
|
130
|
+
if value == nil {
|
|
131
|
+
return "<nil>"
|
|
132
|
+
}
|
|
133
|
+
return "\"" + *value + "\""
|
|
134
|
+
}
|
|
@@ -91,6 +91,15 @@ func MetadataDependency_active(checker *nativechecker.Checker) bool {
|
|
|
91
91
|
return metadataDependency_listener(checker).active()
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
// MetadataDependency_touchFile reports a source file consulted outside the
|
|
95
|
+
// metadata iterator, so a complete transform dependency list still watches it.
|
|
96
|
+
func MetadataDependency_touchFile(checker *nativechecker.Checker, fileName string) {
|
|
97
|
+
listener := metadataDependency_listener(checker)
|
|
98
|
+
if listener.File != nil {
|
|
99
|
+
listener.File(fileName)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
94
103
|
// MetadataDependency_touchType reports the declaration files of a consulted
|
|
95
104
|
// type: both the structural symbol (interface / class / enum / object literal)
|
|
96
105
|
// and the type-name symbol (a `type` alias), which differ for aliased types.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
package utils
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"math"
|
|
5
|
+
"math/big"
|
|
6
|
+
"regexp"
|
|
7
|
+
"strconv"
|
|
8
|
+
"strings"
|
|
9
|
+
"unicode"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// NumberUtil reads numeric text the way JavaScript's `Number()` does.
|
|
13
|
+
//
|
|
14
|
+
// Tag values and JSDoc extensions are written in TypeScript source, and typia
|
|
15
|
+
// splices numbers it reads from them into the JavaScript it emits, so their
|
|
16
|
+
// grammar is JavaScript's StringNumericLiteral, not Go's float syntax. The two
|
|
17
|
+
// differ in both directions: `strconv.ParseFloat` accepts `NaN`, `Inf`, and
|
|
18
|
+
// `infinity` in any case, hexadecimal floats (`0x1p4`), and digit separators
|
|
19
|
+
// (`1_000`), all of which `Number()` reads as NaN, while it rejects the
|
|
20
|
+
// `0x`/`0o`/`0b` integers that `Number()` accepts. A spelling Go accepts but
|
|
21
|
+
// JavaScript does not also becomes invalid emitted code once it is spliced into
|
|
22
|
+
// a validator (samchon/typia#2442).
|
|
23
|
+
type numberUtilNamespace struct{}
|
|
24
|
+
|
|
25
|
+
var NumberUtil = numberUtilNamespace{}
|
|
26
|
+
|
|
27
|
+
// NumberUtil_Reading is the outcome of reading one numeric text.
|
|
28
|
+
type NumberUtil_Reading struct {
|
|
29
|
+
// Value is what `Number(text)` evaluates to, with negative zero read as zero.
|
|
30
|
+
Value float64
|
|
31
|
+
|
|
32
|
+
// Numeric is false when `Number(text)` is NaN, or when the text is blank.
|
|
33
|
+
Numeric bool
|
|
34
|
+
|
|
35
|
+
// Finite is false for `Infinity`, `-Infinity`, and values that overflow to
|
|
36
|
+
// them, such as `1e400`. JSON has no spelling for either.
|
|
37
|
+
Finite bool
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Read evaluates text with the grammar of JavaScript's `Number()`.
|
|
41
|
+
//
|
|
42
|
+
// Surrounding JavaScript whitespace is ignored, as `Number()` ignores it. One
|
|
43
|
+
// deliberate difference remains: `Number("")` and `Number(" ")` are 0, but
|
|
44
|
+
// blank text names no number, so it reads as non-numeric here.
|
|
45
|
+
func (numberUtilNamespace) Read(text string) NumberUtil_Reading {
|
|
46
|
+
text = strings.TrimFunc(text, numberUtil_isWhiteSpace)
|
|
47
|
+
if text == "" {
|
|
48
|
+
return NumberUtil_Reading{}
|
|
49
|
+
}
|
|
50
|
+
if match := numberUtil_NON_DECIMAL.FindStringSubmatch(text); match != nil {
|
|
51
|
+
base := 16
|
|
52
|
+
switch match[1] {
|
|
53
|
+
case "o", "O":
|
|
54
|
+
base = 8
|
|
55
|
+
case "b", "B":
|
|
56
|
+
base = 2
|
|
57
|
+
}
|
|
58
|
+
integer, ok := new(big.Int).SetString(match[2], base)
|
|
59
|
+
if ok == false {
|
|
60
|
+
return NumberUtil_Reading{}
|
|
61
|
+
}
|
|
62
|
+
// big.Float rounds to nearest, ties to even, as the specification's
|
|
63
|
+
// mathematical-value conversion does, and reports overflow as an infinity.
|
|
64
|
+
value, _ := new(big.Float).SetInt(integer).Float64()
|
|
65
|
+
return numberUtil_reading(value)
|
|
66
|
+
}
|
|
67
|
+
if numberUtil_DECIMAL.MatchString(text) == false {
|
|
68
|
+
return NumberUtil_Reading{}
|
|
69
|
+
}
|
|
70
|
+
if strings.HasSuffix(text, "Infinity") {
|
|
71
|
+
if strings.HasPrefix(text, "-") {
|
|
72
|
+
return numberUtil_reading(math.Inf(-1))
|
|
73
|
+
}
|
|
74
|
+
return numberUtil_reading(math.Inf(1))
|
|
75
|
+
}
|
|
76
|
+
// The pattern admits only plain decimal notation, which ParseFloat reads with
|
|
77
|
+
// IEEE round-to-nearest. An out-of-range magnitude comes back as an infinity
|
|
78
|
+
// with ErrRange, which is exactly what `Number()` yields; underflow comes back
|
|
79
|
+
// as zero without an error, as it does in JavaScript.
|
|
80
|
+
value, err := strconv.ParseFloat(text, 64)
|
|
81
|
+
if err != nil && math.IsInf(value, 0) == false {
|
|
82
|
+
return NumberUtil_Reading{}
|
|
83
|
+
}
|
|
84
|
+
return numberUtil_reading(value)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Integer reads text that spells an integer exactly: decimal digits with an
|
|
88
|
+
// optional sign, or an unsigned `0x` / `0o` / `0b` literal.
|
|
89
|
+
//
|
|
90
|
+
// `Number()` rounds such text to the nearest double, so `9007199254740993`
|
|
91
|
+
// reads as 9007199254740992. Splicing the digits as written keeps a check that
|
|
92
|
+
// reads them as a BigInt literal (`$input % 9007199254740993n`) exact. Any
|
|
93
|
+
// other spelling, including an integer written with an exponent or a point
|
|
94
|
+
// (`1e3`, `1.0`), reports false; its value is the double `Read` returns.
|
|
95
|
+
func (numberUtilNamespace) Integer(text string) (*big.Int, bool) {
|
|
96
|
+
text = strings.TrimFunc(text, numberUtil_isWhiteSpace)
|
|
97
|
+
if match := numberUtil_NON_DECIMAL.FindStringSubmatch(text); match != nil {
|
|
98
|
+
base := 16
|
|
99
|
+
switch match[1] {
|
|
100
|
+
case "o", "O":
|
|
101
|
+
base = 8
|
|
102
|
+
case "b", "B":
|
|
103
|
+
base = 2
|
|
104
|
+
}
|
|
105
|
+
return new(big.Int).SetString(match[2], base)
|
|
106
|
+
}
|
|
107
|
+
if numberUtil_INTEGER.MatchString(text) == false {
|
|
108
|
+
return nil, false
|
|
109
|
+
}
|
|
110
|
+
return new(big.Int).SetString(strings.TrimPrefix(text, "+"), 10)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Rational is the exact value a finite numeric text writes.
|
|
114
|
+
//
|
|
115
|
+
// `Read` evaluates the text as `Number()` does, rounding it to the nearest
|
|
116
|
+
// double; this keeps what the text says, so a caller can tell whether that
|
|
117
|
+
// double is the written value itself. `9007199254740993` and
|
|
118
|
+
// `9007199254740993.0` both read as 9007199254740992, but their rational value
|
|
119
|
+
// is 9007199254740993. Text `Read` does not find numeric and finite reports
|
|
120
|
+
// false, and so does text `big.Rat` refuses to expand: an exponent outside the
|
|
121
|
+
// int64 range, or a non-zero mantissa whose exponent, net of its fraction
|
|
122
|
+
// digits, lies beyond a million in magnitude (`1e-1000001` reads as 0 but has
|
|
123
|
+
// no exact value here).
|
|
124
|
+
func (numberUtilNamespace) Rational(text string) (*big.Rat, bool) {
|
|
125
|
+
if reading := NumberUtil.Read(text); reading.Numeric == false || reading.Finite == false {
|
|
126
|
+
return nil, false
|
|
127
|
+
}
|
|
128
|
+
if integer, ok := NumberUtil.Integer(text); ok {
|
|
129
|
+
return new(big.Rat).SetInt(integer), true
|
|
130
|
+
}
|
|
131
|
+
// `Read` admitted only StrDecimalLiteral here, whose spellings `big.Rat`
|
|
132
|
+
// parses: `5.`, `.5`, `+5`, and `1E3` included.
|
|
133
|
+
return new(big.Rat).SetString(strings.TrimFunc(text, numberUtil_isWhiteSpace))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// String spells a value exactly as JavaScript's `String(value)` does.
|
|
137
|
+
//
|
|
138
|
+
// That is `Number.prototype.toString`: positional notation from 1e-6 up to
|
|
139
|
+
// 1e21, exponent notation outside with a signed exponent of no padded zeros
|
|
140
|
+
// (`1e+21`, `1e-7`), and `NaN`, `Infinity`, `-Infinity`, and `0` for negative
|
|
141
|
+
// zero. Each result is also JavaScript source text that evaluates to the value,
|
|
142
|
+
// negative zero reading back as zero.
|
|
143
|
+
func (numberUtilNamespace) String(value float64) string {
|
|
144
|
+
switch {
|
|
145
|
+
case math.IsNaN(value):
|
|
146
|
+
return "NaN"
|
|
147
|
+
case math.IsInf(value, 1):
|
|
148
|
+
return "Infinity"
|
|
149
|
+
case math.IsInf(value, -1):
|
|
150
|
+
return "-Infinity"
|
|
151
|
+
case value == 0:
|
|
152
|
+
return "0"
|
|
153
|
+
}
|
|
154
|
+
magnitude := math.Abs(value)
|
|
155
|
+
if magnitude >= 1e-6 && magnitude < 1e21 {
|
|
156
|
+
return strconv.FormatFloat(value, 'f', -1, 64)
|
|
157
|
+
}
|
|
158
|
+
// Go pads the exponent to two digits (`1e-07`); JavaScript does not.
|
|
159
|
+
text := strconv.FormatFloat(value, 'e', -1, 64)
|
|
160
|
+
index := strings.IndexByte(text, 'e')
|
|
161
|
+
mantissa, sign, digits := text[:index], text[index+1:index+2], strings.TrimLeft(text[index+2:], "0")
|
|
162
|
+
return mantissa + "e" + sign + digits
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
func numberUtil_reading(value float64) NumberUtil_Reading {
|
|
166
|
+
if value == 0 {
|
|
167
|
+
value = 0 // negative zero
|
|
168
|
+
}
|
|
169
|
+
return NumberUtil_Reading{
|
|
170
|
+
Value: value,
|
|
171
|
+
Numeric: true,
|
|
172
|
+
Finite: math.IsInf(value, 0) == false,
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// numberUtil_isWhiteSpace is StrWhiteSpaceChar: WhiteSpace and LineTerminator.
|
|
177
|
+
func numberUtil_isWhiteSpace(r rune) bool {
|
|
178
|
+
switch r {
|
|
179
|
+
case '\t', '\v', '\f', ' ', 0x00a0, 0xfeff, '\n', '\r', 0x2028, 0x2029:
|
|
180
|
+
return true
|
|
181
|
+
}
|
|
182
|
+
return unicode.Is(unicode.Zs, r)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// numberUtil_NON_DECIMAL is NonDecimalIntegerLiteral without separators. It
|
|
186
|
+
// takes no sign: `Number("-0x10")` is NaN.
|
|
187
|
+
var numberUtil_NON_DECIMAL = regexp.MustCompile(`^0([xXoObB])([0-9a-fA-F]+)$`)
|
|
188
|
+
|
|
189
|
+
// numberUtil_INTEGER is a decimal integer with an optional sign.
|
|
190
|
+
var numberUtil_INTEGER = regexp.MustCompile(`^[+-]?[0-9]+$`)
|
|
191
|
+
|
|
192
|
+
// numberUtil_DECIMAL is StrDecimalLiteral: an optional sign before `Infinity`
|
|
193
|
+
// or a decimal literal whose digits may omit either side of the point, with an
|
|
194
|
+
// optional exponent. Separators are not part of it.
|
|
195
|
+
var numberUtil_DECIMAL = regexp.MustCompile(`^[+-]?(?:Infinity|(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:[eE][+-]?[0-9]+)?)$`)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
package utils
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"math/big"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// TestNumberUtilRationalKeepsWrittenValue verifies NumberUtil.Rational keeps the
|
|
9
|
+
// value a numeric text writes, not the double `Number()` rounds it to.
|
|
10
|
+
//
|
|
11
|
+
// A bigint comment tag must know whether a double is the written integer itself
|
|
12
|
+
// before it states a bound with it (samchon/typia#2457), so the exact value has
|
|
13
|
+
// to survive every spelling `Read` admits: points, exponents, signs, radix
|
|
14
|
+
// prefixes, and surrounding whitespace. Text `Read` refuses, or finds infinite,
|
|
15
|
+
// has no exact value.
|
|
16
|
+
//
|
|
17
|
+
// 1. Keep integers past 2^53, in every spelling.
|
|
18
|
+
// 2. Keep fractions and point-edge spellings exactly.
|
|
19
|
+
// 3. Refuse what `Read` refuses, and infinities.
|
|
20
|
+
func TestNumberUtilRationalKeepsWrittenValue(t *testing.T) {
|
|
21
|
+
exact := func(text string) *big.Rat {
|
|
22
|
+
value, ok := new(big.Rat).SetString(text)
|
|
23
|
+
if ok == false {
|
|
24
|
+
t.Fatalf("bad expectation %s", text)
|
|
25
|
+
}
|
|
26
|
+
return value
|
|
27
|
+
}
|
|
28
|
+
for _, item := range []struct {
|
|
29
|
+
text string
|
|
30
|
+
expected string
|
|
31
|
+
}{
|
|
32
|
+
// 1. integers past 2^53
|
|
33
|
+
{"9007199254740993", "9007199254740993"},
|
|
34
|
+
{"9007199254740993.0", "9007199254740993"},
|
|
35
|
+
{"9.007199254740993e15", "9007199254740993"},
|
|
36
|
+
{"+9007199254740993", "9007199254740993"},
|
|
37
|
+
{"-9007199254740993", "-9007199254740993"},
|
|
38
|
+
{"0x20000000000001", "9007199254740993"},
|
|
39
|
+
{"0o400000000000000001", "9007199254740993"},
|
|
40
|
+
{" 9007199254740993\t", "9007199254740993"},
|
|
41
|
+
{"\u00a09007199254740993.0 ", "9007199254740993"},
|
|
42
|
+
{"\t.5\u2028", "1/2"},
|
|
43
|
+
// 2. fractions and point edges
|
|
44
|
+
{".5", "1/2"},
|
|
45
|
+
{"5.", "5"},
|
|
46
|
+
{"-.5e1", "-5"},
|
|
47
|
+
{"1e-3", "1/1000"},
|
|
48
|
+
{"-0", "0"},
|
|
49
|
+
} {
|
|
50
|
+
value, ok := NumberUtil.Rational(item.text)
|
|
51
|
+
if ok == false || value.Cmp(exact(item.expected)) != 0 {
|
|
52
|
+
t.Fatalf("%q must be exactly %s, got %v (ok=%v)", item.text, item.expected, value, ok)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 3. what Read refuses, and infinities
|
|
57
|
+
for _, text := range []string{"", " ", "1_000", "0x1p4", "inf", "NaN", "-0x10", "Infinity", "-Infinity", "1e400"} {
|
|
58
|
+
if value, ok := NumberUtil.Rational(text); ok {
|
|
59
|
+
t.Fatalf("%q must have no exact value, got %v", text, value)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|