typia 15.0.0 → 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/lib/http.d.ts +49 -24
- package/lib/http.js.map +1 -1
- package/lib/http.mjs.map +1 -1
- 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/native/cmd/ttsc-typia/llm_evaluation_rejects_invalid_probability_test.go +9 -0
- package/native/cmd/ttsc-typia/random_recursive_min_items_diagnostic_test.go +18 -1
- package/native/core/factories/ExpressionFactory.go +16 -1
- package/native/core/factories/MetadataCommentTagFactory.go +177 -29
- package/native/core/factories/expression_factory_number_spells_non_finite_values_test.go +69 -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/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/LlmEvaluationProgrammer.go +6 -2
- package/native/core/programmers/llm/LlmSchemaProgrammer.go +34 -4
- 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/package.json +3 -3
- package/src/http.ts +61 -34
- 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
|
@@ -30,6 +30,14 @@ type HttpQueryProgrammer_DecomposeProps struct {
|
|
|
30
30
|
AllowOptional bool
|
|
31
31
|
Type *shimchecker.Type
|
|
32
32
|
Name *string
|
|
33
|
+
|
|
34
|
+
// Missing makes the decoder throw `Error("missing <key>")` for an absent
|
|
35
|
+
// required non-array property. Only `http.query`, which validates nothing
|
|
36
|
+
// else, asks for it. The `is`, `assert`, and `validate` variants decode
|
|
37
|
+
// through this same function and must report the absence through their own
|
|
38
|
+
// contract instead (`null`, an `IValidation` failure, `TypeGuardError`), so
|
|
39
|
+
// they leave it off (samchon/typia#2444).
|
|
40
|
+
Missing bool
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
var httpQueryProgrammer_factory = shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
|
|
@@ -78,9 +86,11 @@ func (httpQueryProgrammerNamespace) Decompose(props HttpQueryProgrammer_Decompos
|
|
|
78
86
|
statements := httpQueryProgrammer_decode_object(struct {
|
|
79
87
|
Context nativecontext.ITypiaContext
|
|
80
88
|
Object *schemametadata.MetadataObjectType
|
|
89
|
+
Missing bool
|
|
81
90
|
}{
|
|
82
91
|
Context: props.Context,
|
|
83
92
|
Object: object,
|
|
93
|
+
Missing: props.Missing,
|
|
84
94
|
})
|
|
85
95
|
return nativeinternal.FeatureProgrammer_IDecomposed{
|
|
86
96
|
Functions: map[string]*shimast.Node{},
|
|
@@ -124,6 +134,7 @@ func (httpQueryProgrammerNamespace) Write(props HttpQueryProgrammer_IProps) *shi
|
|
|
124
134
|
AllowOptional: props.AllowOptional,
|
|
125
135
|
Type: props.Type,
|
|
126
136
|
Name: props.Name,
|
|
137
|
+
Missing: true,
|
|
127
138
|
})
|
|
128
139
|
return nativeinternal.FeatureProgrammer.WriteDecomposed(nativeinternal.FeatureProgrammer_WriteDecomposedProps{
|
|
129
140
|
Modulo: props.Modulo,
|
|
@@ -203,6 +214,7 @@ func (httpQueryProgrammerNamespace) Validate(props struct {
|
|
|
203
214
|
func httpQueryProgrammer_decode_object(props struct {
|
|
204
215
|
Context nativecontext.ITypiaContext
|
|
205
216
|
Object *schemametadata.MetadataObjectType
|
|
217
|
+
Missing bool
|
|
206
218
|
}) []*shimast.Node {
|
|
207
219
|
f := nativecontext.EmitFactoryOf(httpQueryProgrammer_factory, props.Context.Emit)
|
|
208
220
|
input := f.NewIdentifier("input")
|
|
@@ -212,9 +224,11 @@ func httpQueryProgrammer_decode_object(props struct {
|
|
|
212
224
|
properties = append(properties, httpQueryProgrammer_decode_regular_property(struct {
|
|
213
225
|
Context nativecontext.ITypiaContext
|
|
214
226
|
Property *schemametadata.MetadataProperty
|
|
227
|
+
Missing bool
|
|
215
228
|
}{
|
|
216
229
|
Context: props.Context,
|
|
217
230
|
Property: p,
|
|
231
|
+
Missing: props.Missing,
|
|
218
232
|
}))
|
|
219
233
|
}
|
|
220
234
|
return []*shimast.Node{
|
|
@@ -253,6 +267,7 @@ func httpQueryProgrammer_decode_object(props struct {
|
|
|
253
267
|
func httpQueryProgrammer_decode_regular_property(props struct {
|
|
254
268
|
Context nativecontext.ITypiaContext
|
|
255
269
|
Property *schemametadata.MetadataProperty
|
|
270
|
+
Missing bool
|
|
256
271
|
}) *shimast.Node {
|
|
257
272
|
f := nativecontext.EmitFactoryOf(httpQueryProgrammer_factory, props.Context.Emit)
|
|
258
273
|
key := httpProgrammer_property_key(props.Property)
|
|
@@ -277,9 +292,9 @@ func httpQueryProgrammer_decode_regular_property(props struct {
|
|
|
277
292
|
}),
|
|
278
293
|
shimast.NodeFlagsNone,
|
|
279
294
|
)
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
295
|
+
// An absent key reads as an empty list. A query string has no other
|
|
296
|
+
// spelling for `[]`, so a required array decodes it as `[]` rather than as
|
|
297
|
+
// a missing property, and a nullable one as `null` (samchon/typia#2444).
|
|
283
298
|
input = f.NewCallExpression(
|
|
284
299
|
nativefactories.IdentifierFactory.Access(
|
|
285
300
|
props.Context.Emit,
|
|
@@ -301,11 +316,13 @@ func httpQueryProgrammer_decode_regular_property(props struct {
|
|
|
301
316
|
httpQueryProgrammer_decode_value(struct {
|
|
302
317
|
Context nativecontext.ITypiaContext
|
|
303
318
|
Type string
|
|
319
|
+
Nullable bool
|
|
304
320
|
Coalesce bool
|
|
305
321
|
Input *shimast.Node
|
|
306
322
|
}{
|
|
307
323
|
Context: props.Context,
|
|
308
324
|
Type: typ,
|
|
325
|
+
Nullable: httpProgrammer_decode_nullable(value),
|
|
309
326
|
Coalesce: false,
|
|
310
327
|
Input: f.NewIdentifier("elem"),
|
|
311
328
|
}),
|
|
@@ -323,18 +340,20 @@ func httpQueryProgrammer_decode_regular_property(props struct {
|
|
|
323
340
|
Input: input,
|
|
324
341
|
})
|
|
325
342
|
} else {
|
|
326
|
-
if value.IsRequired() {
|
|
343
|
+
if value.IsRequired() && props.Missing {
|
|
327
344
|
input = httpQueryProgrammer_assert_required_value(key, input, props.Context.Emit)
|
|
328
345
|
}
|
|
329
346
|
input = httpQueryProgrammer_decode_value(struct {
|
|
330
347
|
Context nativecontext.ITypiaContext
|
|
331
348
|
Type string
|
|
349
|
+
Nullable bool
|
|
332
350
|
Coalesce bool
|
|
333
351
|
Input *shimast.Node
|
|
334
352
|
}{
|
|
335
353
|
Context: props.Context,
|
|
336
354
|
Type: typ,
|
|
337
|
-
|
|
355
|
+
Nullable: httpProgrammer_decode_nullable(value),
|
|
356
|
+
Coalesce: value.Nullable == false && value.Any == false && value.IsRequired() == false,
|
|
338
357
|
Input: input,
|
|
339
358
|
})
|
|
340
359
|
}
|
|
@@ -350,6 +369,7 @@ func httpQueryProgrammer_decode_regular_property(props struct {
|
|
|
350
369
|
func httpQueryProgrammer_decode_value(props struct {
|
|
351
370
|
Context nativecontext.ITypiaContext
|
|
352
371
|
Type string
|
|
372
|
+
Nullable bool
|
|
353
373
|
Coalesce bool
|
|
354
374
|
Input *shimast.Node
|
|
355
375
|
}) *shimast.Node {
|
|
@@ -358,7 +378,7 @@ func httpQueryProgrammer_decode_value(props struct {
|
|
|
358
378
|
httpParameterProgrammer_internal(props.Context, "httpQueryRead"+httpParameterProgrammer_capitalize(props.Type)),
|
|
359
379
|
nil,
|
|
360
380
|
nil,
|
|
361
|
-
f.NewNodeList(
|
|
381
|
+
f.NewNodeList(httpProgrammer_read_arguments(props.Type, props.Nullable, props.Input, props.Context.Emit)),
|
|
362
382
|
shimast.NodeFlagsNone,
|
|
363
383
|
)
|
|
364
384
|
if props.Coalesce == false {
|
|
@@ -410,43 +430,6 @@ func httpQueryProgrammer_assert_required_value(key string, input *shimast.Node,
|
|
|
410
430
|
)
|
|
411
431
|
}
|
|
412
432
|
|
|
413
|
-
func httpQueryProgrammer_assert_required_array(key string, input *shimast.Node, emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
414
|
-
f := nativecontext.EmitFactoryOf(httpQueryProgrammer_factory, emit...)
|
|
415
|
-
value := f.NewIdentifier("value")
|
|
416
|
-
return f.NewCallExpression(
|
|
417
|
-
f.NewArrowFunction(
|
|
418
|
-
nil,
|
|
419
|
-
nil,
|
|
420
|
-
f.NewNodeList(nil),
|
|
421
|
-
nil,
|
|
422
|
-
nil,
|
|
423
|
-
f.NewToken(shimast.KindEqualsGreaterThanToken),
|
|
424
|
-
f.NewBlock(f.NewNodeList([]*shimast.Node{
|
|
425
|
-
nativefactories.StatementFactory.Constant(nativefactories.StatementFactory_ConstantProps{
|
|
426
|
-
Name: "value",
|
|
427
|
-
Value: input,
|
|
428
|
-
}, emit...),
|
|
429
|
-
f.NewIfStatement(
|
|
430
|
-
f.NewBinaryExpression(
|
|
431
|
-
nil,
|
|
432
|
-
nativefactories.ExpressionFactory.Number(0, emit...),
|
|
433
|
-
nil,
|
|
434
|
-
f.NewToken(shimast.KindEqualsEqualsEqualsToken),
|
|
435
|
-
nativefactories.IdentifierFactory.Access(nil, value, "length"),
|
|
436
|
-
),
|
|
437
|
-
f.NewThrowStatement(httpQueryProgrammer_missing_error(key, emit...)),
|
|
438
|
-
nil,
|
|
439
|
-
),
|
|
440
|
-
f.NewReturnStatement(value),
|
|
441
|
-
}), true),
|
|
442
|
-
),
|
|
443
|
-
nil,
|
|
444
|
-
nil,
|
|
445
|
-
nil,
|
|
446
|
-
shimast.NodeFlagsNone,
|
|
447
|
-
)
|
|
448
|
-
}
|
|
449
|
-
|
|
450
433
|
func httpQueryProgrammer_missing_error(key string, emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
451
434
|
f := nativecontext.EmitFactoryOf(httpQueryProgrammer_factory, emit...)
|
|
452
435
|
return f.NewNewExpression(
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
package iterate
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
"strconv"
|
|
5
4
|
"strings"
|
|
6
5
|
|
|
7
6
|
nativemetadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
|
|
7
|
+
nativeutils "github.com/samchon/typia/packages/typia/native/core/utils"
|
|
8
8
|
)
|
|
9
9
|
|
|
10
10
|
func json_schema_jsDocTags(schema JsonSchema, jsDocTags []nativemetadata.IJsDocTagInfo) JsonSchema {
|
|
@@ -31,6 +31,13 @@ func json_schema_jsDocTags_value(tag nativemetadata.IJsDocTagInfo) (string, bool
|
|
|
31
31
|
return "", false
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// json_schema_jsDocTags_cast reads an `@x-*` tag's text as a JSON value.
|
|
35
|
+
//
|
|
36
|
+
// Numbers use JavaScript's `Number()` grammar, the grammar of the v12 cast this
|
|
37
|
+
// ports (samchon/typia#2442), so `0x10` is 16 while Go-only spellings such as
|
|
38
|
+
// `0x1p4`, `1_000`, and `inf` stay text. Only finite numbers become numbers: the
|
|
39
|
+
// schema is JSON, which has no NaN or infinity, so `NaN`, `Infinity`, and an
|
|
40
|
+
// overflowing `1e400` keep their text instead of degrading to `null`.
|
|
34
41
|
func json_schema_jsDocTags_cast(value string) any {
|
|
35
42
|
if value == "true" {
|
|
36
43
|
return true
|
|
@@ -38,8 +45,8 @@ func json_schema_jsDocTags_cast(value string) any {
|
|
|
38
45
|
if value == "false" {
|
|
39
46
|
return false
|
|
40
47
|
}
|
|
41
|
-
if
|
|
42
|
-
return
|
|
48
|
+
if reading := nativeutils.NumberUtil.Read(value); reading.Numeric && reading.Finite {
|
|
49
|
+
return reading.Value
|
|
43
50
|
}
|
|
44
51
|
if value == "null" {
|
|
45
52
|
return nil
|
package/native/core/programmers/iterate/json_schema_jsdoc_tags_cast_reads_javascript_numbers_test.go
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
package iterate
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"math"
|
|
6
|
+
"reflect"
|
|
7
|
+
"testing"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestJsonSchemaJsDocTagsCastReadsJavaScriptNumbers verifies the `@x-*`
|
|
11
|
+
// extension cast.
|
|
12
|
+
//
|
|
13
|
+
// The Go port swapped the v12 cast's JavaScript `Number()` grammar for Go's
|
|
14
|
+
// float syntax (samchon/typia#2442). `NaN` and every `Inf` spelling then became
|
|
15
|
+
// floats that `encoding/json` refuses and JavaScript prints as `null`, while
|
|
16
|
+
// `0x10` stopped being a number and `0x1p4` / `1_000` started being one. The
|
|
17
|
+
// expectations restore v12's numbers where they are finite and keep the text
|
|
18
|
+
// where JSON has no number: a non-finite spelling stays a string instead of
|
|
19
|
+
// degrading to `null`, and empty text stays `""` instead of v12's `0`.
|
|
20
|
+
//
|
|
21
|
+
// 1. Cast every row of the issue's table.
|
|
22
|
+
// 2. Assert no result is NaN or an infinity, and every result marshals.
|
|
23
|
+
func TestJsonSchemaJsDocTagsCastReadsJavaScriptNumbers(t *testing.T) {
|
|
24
|
+
for _, item := range []struct {
|
|
25
|
+
text string
|
|
26
|
+
expected any
|
|
27
|
+
}{
|
|
28
|
+
{"NaN", "NaN"},
|
|
29
|
+
{"nan", "nan"},
|
|
30
|
+
{"Infinity", "Infinity"},
|
|
31
|
+
{"-Infinity", "-Infinity"},
|
|
32
|
+
{"infinity", "infinity"},
|
|
33
|
+
{"inf", "inf"},
|
|
34
|
+
{"Inf", "Inf"},
|
|
35
|
+
{"1e400", "1e400"},
|
|
36
|
+
{"0x10", float64(16)},
|
|
37
|
+
{"0b101", float64(5)},
|
|
38
|
+
{"0o7", float64(7)},
|
|
39
|
+
{"0x1p4", "0x1p4"},
|
|
40
|
+
{"1_000", "1_000"},
|
|
41
|
+
{"", ""},
|
|
42
|
+
{"-0", float64(0)},
|
|
43
|
+
{"+5", float64(5)},
|
|
44
|
+
{".5", 0.5},
|
|
45
|
+
{"1e3", float64(1000)},
|
|
46
|
+
{"true", true},
|
|
47
|
+
{"false", false},
|
|
48
|
+
{"null", nil},
|
|
49
|
+
{"text", "text"},
|
|
50
|
+
} {
|
|
51
|
+
actual := json_schema_jsDocTags_cast(item.text)
|
|
52
|
+
if reflect.DeepEqual(actual, item.expected) == false {
|
|
53
|
+
t.Fatalf("cast(%q) = %#v, expected %#v", item.text, actual, item.expected)
|
|
54
|
+
}
|
|
55
|
+
if number, ok := actual.(float64); ok {
|
|
56
|
+
if math.IsNaN(number) || math.IsInf(number, 0) || math.Signbit(number) && number == 0 {
|
|
57
|
+
t.Fatalf("cast(%q) = %v is not a JSON number", item.text, number)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if _, err := json.Marshal(actual); err != nil {
|
|
61
|
+
t.Fatalf("cast(%q) does not marshal: %v", item.text, err)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -12,6 +12,7 @@ import (
|
|
|
12
12
|
nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
|
|
13
13
|
nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
|
|
14
14
|
schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
|
|
15
|
+
nativeutils "github.com/samchon/typia/packages/typia/native/core/utils"
|
|
15
16
|
)
|
|
16
17
|
|
|
17
18
|
type llmEvaluationProgrammerNamespace struct{}
|
|
@@ -449,10 +450,13 @@ func llmEvaluation_parse_jsdoc_probability(tags []schemametadata.IJsDocTagInfo)
|
|
|
449
450
|
if len(texts) != 1 {
|
|
450
451
|
return 0, false, "LLM evaluation @probability is declared more than once; keep only one."
|
|
451
452
|
}
|
|
452
|
-
|
|
453
|
-
|
|
453
|
+
// The same JavaScript `Number()` grammar every other numeric JSDoc tag reads
|
|
454
|
+
// (samchon/typia#2442); Go's float syntax would also admit `0x1p-1`.
|
|
455
|
+
reading := nativeutils.NumberUtil.Read(texts[0])
|
|
456
|
+
if reading.Numeric == false || reading.Finite == false {
|
|
454
457
|
return 0, false, fmt.Sprintf("LLM evaluation @probability must be a number in [0, 1], but got %q.", texts[0])
|
|
455
458
|
}
|
|
459
|
+
value := reading.Value
|
|
456
460
|
if value < 0 || value > 1 {
|
|
457
461
|
return 0, false, fmt.Sprintf("LLM evaluation @probability must be in [0, 1], but got %s.", texts[0])
|
|
458
462
|
}
|
|
@@ -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,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
|
+
}
|