@neocompose/cli 0.31.17 → 0.32.0
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.32.0] - 2026-08-18
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- P69 Math builtins: call `Math.Min`, `Max`, `Clamp`, `Round`, `Floor`,
|
|
8
|
+
`Ceiling`, `Truncate`, `Abs`, `Sign`, and `Sqrt`, and read the `Math.PI` and
|
|
9
|
+
`Math.E` constants, from any NeoScript body. Results are inferred from the
|
|
10
|
+
argument types under the arithmetic join rule, `Round`/`Floor`/`Ceiling`/
|
|
11
|
+
`Truncate` return `int` on `int` and `float` input, and `Math` is now a
|
|
12
|
+
reserved name.
|
|
13
|
+
|
|
3
14
|
## [0.31.17] - 2026-08-18
|
|
4
15
|
|
|
5
16
|
### Fixed
|
package/dist/neo.mjs
CHANGED
|
@@ -1308,6 +1308,20 @@ function structuredLeafFieldProperties(owner) {
|
|
|
1308
1308
|
(field) => property(owner, field.name, field.type, field.documentation, true)
|
|
1309
1309
|
);
|
|
1310
1310
|
}
|
|
1311
|
+
function mathFunction(name, parameterNames, documentation, returnType = FLOAT_TYPE) {
|
|
1312
|
+
return {
|
|
1313
|
+
id: `${NEOSCRIPT_MATH_TYPE_ID}:function:${name}`,
|
|
1314
|
+
name,
|
|
1315
|
+
kind: "function",
|
|
1316
|
+
type: returnType,
|
|
1317
|
+
returnType,
|
|
1318
|
+
parameters: parameterNames.map(
|
|
1319
|
+
(parameterName) => parameter(parameterName, FLOAT_TYPE)
|
|
1320
|
+
),
|
|
1321
|
+
documentation,
|
|
1322
|
+
static: true
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1311
1325
|
function vectorTypes() {
|
|
1312
1326
|
const names = [
|
|
1313
1327
|
"Vector2",
|
|
@@ -1322,7 +1336,7 @@ function vectorTypes() {
|
|
|
1322
1336
|
members: structuredLeafFieldProperties(name)
|
|
1323
1337
|
}));
|
|
1324
1338
|
}
|
|
1325
|
-
var NEO_PROJECT_IGNORED_DIRECTORY_NAMES, NEOSCRIPT_KEYWORDS, NEOSCRIPT_INFERRED_LOCAL_KEYWORD, NEOSCRIPT_CONSTRUCTOR_KEYWORD, NEOSCRIPT_PRIMITIVE_TYPES, NEOSCRIPT_BUILTIN_TYPES, NEO_VARIANT_TYPE_NAME, NEO_VARIANT_FOLDER_TYPE_NAME, NEO_LOOKUP_VARIANT_TYPE_NAME, NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME, NEO_VARIANT_SCOPE_NAME, NEO_VARIANT_RESERVED_NAME, NEO_VARIANT_FOLDER_PATH_SEPARATOR, NEOSCRIPT_OPERATORS, NEOSCRIPT_STATEMENT_SNIPPETS, STRING_TYPE, INT_TYPE, FLOAT_TYPE, BOOL_TYPE, IMAGE_REF_TYPE, SPRITE_INFO_TYPE, FLOAT_OPTIONAL_TYPE, NEOSCRIPT_STRUCTURED_LEAF_FIELDS, NEOSCRIPT_IMAGE_REGISTRY_TYPE, NEOSCRIPT_AUDIO_CLIP_REGISTRY_TYPE, NEOSCRIPT_PENDING_SYMBOL_ID_PREFIX, REGISTRY_ENTRY_TYPES, NEOSCRIPT_GLOBALS, NEOSCRIPT_SPRITE_INFO_EMPTY_MEMBER_ID, NEOSCRIPT_SPRITE_INFO_EMPTY_VALUE, NEOSCRIPT_BUILTIN_TYPE_SYMBOLS;
|
|
1339
|
+
var NEO_PROJECT_IGNORED_DIRECTORY_NAMES, NEOSCRIPT_KEYWORDS, NEOSCRIPT_INFERRED_LOCAL_KEYWORD, NEOSCRIPT_CONSTRUCTOR_KEYWORD, NEOSCRIPT_PRIMITIVE_TYPES, NEOSCRIPT_BUILTIN_ANNOTATION_TYPES, NEOSCRIPT_BUILTIN_NAMESPACES, NEOSCRIPT_BUILTIN_TYPES, NEO_VARIANT_TYPE_NAME, NEO_VARIANT_FOLDER_TYPE_NAME, NEO_LOOKUP_VARIANT_TYPE_NAME, NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME, NEO_VARIANT_SCOPE_NAME, NEO_VARIANT_RESERVED_NAME, NEO_VARIANT_FOLDER_PATH_SEPARATOR, NEOSCRIPT_OPERATORS, NEOSCRIPT_STATEMENT_SNIPPETS, STRING_TYPE, INT_TYPE, FLOAT_TYPE, BOOL_TYPE, IMAGE_REF_TYPE, SPRITE_INFO_TYPE, FLOAT_OPTIONAL_TYPE, NEOSCRIPT_STRUCTURED_LEAF_FIELDS, NEOSCRIPT_IMAGE_REGISTRY_TYPE, NEOSCRIPT_AUDIO_CLIP_REGISTRY_TYPE, NEOSCRIPT_PENDING_SYMBOL_ID_PREFIX, REGISTRY_ENTRY_TYPES, NEOSCRIPT_GLOBALS, NEOSCRIPT_SPRITE_INFO_EMPTY_MEMBER_ID, NEOSCRIPT_SPRITE_INFO_EMPTY_VALUE, NEOSCRIPT_MATH_TYPE_ID, MATH_INFERENCE_RULE, MATH_INTEGRAL_RULE, NEOSCRIPT_BUILTIN_TYPE_SYMBOLS;
|
|
1326
1340
|
var init_language_spec = __esm({
|
|
1327
1341
|
"../packages/neoscript-language/src/language-spec.ts"() {
|
|
1328
1342
|
"use strict";
|
|
@@ -1384,7 +1398,7 @@ var init_language_spec = __esm({
|
|
|
1384
1398
|
"bool",
|
|
1385
1399
|
"string"
|
|
1386
1400
|
];
|
|
1387
|
-
|
|
1401
|
+
NEOSCRIPT_BUILTIN_ANNOTATION_TYPES = [
|
|
1388
1402
|
"Dictionary",
|
|
1389
1403
|
"List",
|
|
1390
1404
|
"Set",
|
|
@@ -1410,6 +1424,11 @@ var init_language_spec = __esm({
|
|
|
1410
1424
|
"Vector3Int",
|
|
1411
1425
|
"Color"
|
|
1412
1426
|
];
|
|
1427
|
+
NEOSCRIPT_BUILTIN_NAMESPACES = ["Math"];
|
|
1428
|
+
NEOSCRIPT_BUILTIN_TYPES = [
|
|
1429
|
+
...NEOSCRIPT_BUILTIN_ANNOTATION_TYPES,
|
|
1430
|
+
...NEOSCRIPT_BUILTIN_NAMESPACES
|
|
1431
|
+
];
|
|
1413
1432
|
NEO_VARIANT_TYPE_NAME = "NeoVariant";
|
|
1414
1433
|
NEO_VARIANT_FOLDER_TYPE_NAME = "NeoVariantFolder";
|
|
1415
1434
|
NEO_LOOKUP_VARIANT_TYPE_NAME = "NeoLookupVariant";
|
|
@@ -1658,6 +1677,9 @@ var init_language_spec = __esm({
|
|
|
1658
1677
|
fileId: "",
|
|
1659
1678
|
sliceIndex: 0
|
|
1660
1679
|
});
|
|
1680
|
+
NEOSCRIPT_MATH_TYPE_ID = "builtin:Math";
|
|
1681
|
+
MATH_INFERENCE_RULE = "Accepts int, float, or decimal; the result mirrors the widest argument type.";
|
|
1682
|
+
MATH_INTEGRAL_RULE = "Accepts int, float, or decimal; int and float return int, decimal returns decimal.";
|
|
1661
1683
|
NEOSCRIPT_BUILTIN_TYPE_SYMBOLS = [
|
|
1662
1684
|
{
|
|
1663
1685
|
id: "builtin:SpriteInfo",
|
|
@@ -1730,6 +1752,87 @@ var init_language_spec = __esm({
|
|
|
1730
1752
|
name: "Color",
|
|
1731
1753
|
kind: "builtin",
|
|
1732
1754
|
members: structuredLeafFieldProperties("Color")
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
id: NEOSCRIPT_MATH_TYPE_ID,
|
|
1758
|
+
name: "Math",
|
|
1759
|
+
kind: "builtin",
|
|
1760
|
+
documentation: "Numeric builtins with C# names and C# semantics. Math is a qualifier only \u2014 it names no type and holds no value, and every member is a pure function or constant.",
|
|
1761
|
+
members: [
|
|
1762
|
+
mathFunction(
|
|
1763
|
+
"Min",
|
|
1764
|
+
["a", "b"],
|
|
1765
|
+
`Returns the smaller of two values. ${MATH_INFERENCE_RULE} Ties return the first argument.`
|
|
1766
|
+
),
|
|
1767
|
+
mathFunction(
|
|
1768
|
+
"Max",
|
|
1769
|
+
["a", "b"],
|
|
1770
|
+
`Returns the larger of two values. ${MATH_INFERENCE_RULE} Ties return the first argument.`
|
|
1771
|
+
),
|
|
1772
|
+
mathFunction(
|
|
1773
|
+
"Clamp",
|
|
1774
|
+
["value", "min", "max"],
|
|
1775
|
+
`Returns value pinned to the inclusive range min..max. ${MATH_INFERENCE_RULE} Requires min <= max.`
|
|
1776
|
+
),
|
|
1777
|
+
mathFunction(
|
|
1778
|
+
"Round",
|
|
1779
|
+
["x"],
|
|
1780
|
+
`Rounds to the nearest integer; midpoints round to even (banker's rounding). ${MATH_INTEGRAL_RULE}`,
|
|
1781
|
+
INT_TYPE
|
|
1782
|
+
),
|
|
1783
|
+
mathFunction(
|
|
1784
|
+
"Floor",
|
|
1785
|
+
["x"],
|
|
1786
|
+
`Rounds toward negative infinity. ${MATH_INTEGRAL_RULE}`,
|
|
1787
|
+
INT_TYPE
|
|
1788
|
+
),
|
|
1789
|
+
mathFunction(
|
|
1790
|
+
"Ceiling",
|
|
1791
|
+
["x"],
|
|
1792
|
+
`Rounds toward positive infinity. ${MATH_INTEGRAL_RULE}`,
|
|
1793
|
+
INT_TYPE
|
|
1794
|
+
),
|
|
1795
|
+
mathFunction(
|
|
1796
|
+
"Truncate",
|
|
1797
|
+
["x"],
|
|
1798
|
+
`Rounds toward zero. ${MATH_INTEGRAL_RULE}`,
|
|
1799
|
+
INT_TYPE
|
|
1800
|
+
),
|
|
1801
|
+
mathFunction(
|
|
1802
|
+
"Abs",
|
|
1803
|
+
["x"],
|
|
1804
|
+
`Returns the absolute value. ${MATH_INFERENCE_RULE}`
|
|
1805
|
+
),
|
|
1806
|
+
mathFunction(
|
|
1807
|
+
"Sign",
|
|
1808
|
+
["x"],
|
|
1809
|
+
"Returns -1, 0, or 1 for a negative, zero, or positive value. Accepts int, float, or decimal and always returns int.",
|
|
1810
|
+
INT_TYPE
|
|
1811
|
+
),
|
|
1812
|
+
mathFunction(
|
|
1813
|
+
"Sqrt",
|
|
1814
|
+
["x"],
|
|
1815
|
+
"Returns the square root. Accepts int or float and returns float; does not accept decimal."
|
|
1816
|
+
),
|
|
1817
|
+
{
|
|
1818
|
+
...property(
|
|
1819
|
+
"Math",
|
|
1820
|
+
"PI",
|
|
1821
|
+
FLOAT_TYPE,
|
|
1822
|
+
"The ratio of a circle's circumference to its diameter (3.141592653589793)."
|
|
1823
|
+
),
|
|
1824
|
+
static: true
|
|
1825
|
+
},
|
|
1826
|
+
{
|
|
1827
|
+
...property(
|
|
1828
|
+
"Math",
|
|
1829
|
+
"E",
|
|
1830
|
+
FLOAT_TYPE,
|
|
1831
|
+
"The base of the natural logarithm (2.718281828459045)."
|
|
1832
|
+
),
|
|
1833
|
+
static: true
|
|
1834
|
+
}
|
|
1835
|
+
]
|
|
1733
1836
|
}
|
|
1734
1837
|
];
|
|
1735
1838
|
}
|
|
@@ -2320,12 +2423,11 @@ function complete(snapshot, position) {
|
|
|
2320
2423
|
word
|
|
2321
2424
|
);
|
|
2322
2425
|
} else {
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
candidates = completionItemsForResolution(snapshot, resolved, word);
|
|
2426
|
+
candidates = completionItemsForResolution(
|
|
2427
|
+
snapshot,
|
|
2428
|
+
resolveChain(snapshot, receiverTokens, offset),
|
|
2429
|
+
word
|
|
2430
|
+
);
|
|
2329
2431
|
}
|
|
2330
2432
|
} else if (tail?.kind === "identifier" && tail.text === NEOSCRIPT_CONSTRUCTOR_KEYWORD) {
|
|
2331
2433
|
candidates = constructorCompletionItems(
|
|
@@ -3205,6 +3307,7 @@ function typeCompletionItems(snapshot, word) {
|
|
|
3205
3307
|
)
|
|
3206
3308
|
);
|
|
3207
3309
|
for (const type of snapshot.project.typeByName.values()) {
|
|
3310
|
+
if (type.id === NEOSCRIPT_MATH_TYPE_ID) continue;
|
|
3208
3311
|
items.push(typeCompletion(type, word, snapshot));
|
|
3209
3312
|
}
|
|
3210
3313
|
return items;
|
|
@@ -3409,9 +3512,16 @@ function switchCaseEnumContext(snapshot, offset) {
|
|
|
3409
3512
|
function resolveChain(snapshot, tokens, offset) {
|
|
3410
3513
|
if (tokens.length === 0) return null;
|
|
3411
3514
|
const constructorType = tokens[0]?.kind === "identifier" && tokens[0].text === NEOSCRIPT_CONSTRUCTOR_KEYWORD && tokens[1]?.kind === "identifier" ? snapshot.project.typeByName.get(tokens[1].text) : void 0;
|
|
3515
|
+
const staticRoot = tokens[0]?.kind === "type" ? snapshot.project.typeByName.get(tokens[0].text) : void 0;
|
|
3412
3516
|
let current;
|
|
3413
3517
|
let cursor;
|
|
3414
|
-
if (
|
|
3518
|
+
if (staticRoot) {
|
|
3519
|
+
current = {
|
|
3520
|
+
type: { kind: "named", typeId: staticRoot.id },
|
|
3521
|
+
staticType: staticRoot
|
|
3522
|
+
};
|
|
3523
|
+
cursor = 1;
|
|
3524
|
+
} else if (constructorType?.constructorSignature) {
|
|
3415
3525
|
current = { type: constructorType.constructorSignature.returnType };
|
|
3416
3526
|
const open = tokens.findIndex(
|
|
3417
3527
|
(token, index) => index >= 2 && token.kind === "punctuation" && token.text === "("
|
|
@@ -4509,6 +4619,7 @@ var init_analyzer = __esm({
|
|
|
4509
4619
|
RESERVED_NAMES = /* @__PURE__ */ new Set([
|
|
4510
4620
|
...NEOSCRIPT_KEYWORDS,
|
|
4511
4621
|
...NEOSCRIPT_PRIMITIVE_TYPES,
|
|
4622
|
+
...NEOSCRIPT_BUILTIN_NAMESPACES,
|
|
4512
4623
|
"Dictionary",
|
|
4513
4624
|
"Set"
|
|
4514
4625
|
]);
|
|
@@ -8867,6 +8978,30 @@ function numericResult(left, right) {
|
|
|
8867
8978
|
}
|
|
8868
8979
|
return { kind: "primitive", name: "int" };
|
|
8869
8980
|
}
|
|
8981
|
+
function isContextualNumericLiteral(expression) {
|
|
8982
|
+
if (expression.kind === "litInt" || expression.kind === "litFloat") {
|
|
8983
|
+
return true;
|
|
8984
|
+
}
|
|
8985
|
+
return expression.kind === "unary" && expression.op === "-" && (expression.operand.kind === "litInt" || expression.operand.kind === "litFloat");
|
|
8986
|
+
}
|
|
8987
|
+
function mathResultType(rule, joined, pos) {
|
|
8988
|
+
switch (rule) {
|
|
8989
|
+
case "join":
|
|
8990
|
+
return joined;
|
|
8991
|
+
case "integral":
|
|
8992
|
+
return isPrimitive(joined, "decimal") ? joined : { kind: "primitive", name: "int" };
|
|
8993
|
+
case "int":
|
|
8994
|
+
return { kind: "primitive", name: "int" };
|
|
8995
|
+
case "float":
|
|
8996
|
+
if (isPrimitive(joined, "decimal")) {
|
|
8997
|
+
throw new CompileError(
|
|
8998
|
+
"Math.Sqrt does not accept decimal; convert explicitly with value.ToFloat().",
|
|
8999
|
+
pos
|
|
9000
|
+
);
|
|
9001
|
+
}
|
|
9002
|
+
return { kind: "primitive", name: "float" };
|
|
9003
|
+
}
|
|
9004
|
+
}
|
|
8870
9005
|
function normalizedReturnType(type, kind) {
|
|
8871
9006
|
if (kind === "action" || kind === "setter" || kind === "constructor") {
|
|
8872
9007
|
return { kind: "primitive", name: "null" };
|
|
@@ -9209,7 +9344,7 @@ function switchValueKey(value) {
|
|
|
9209
9344
|
const enumId = value.typeInfo.type === 8 /* Enum */ ? value.typeInfo.enumId : null;
|
|
9210
9345
|
return JSON.stringify([value.typeInfo.type, enumId, value.value]);
|
|
9211
9346
|
}
|
|
9212
|
-
var CONSTRUCTING_FUNCTION_KINDS, VARIANT_INITIALIZE_NAME, VARIANT_APPLY_NAME, WRITE_TARGET_DESCRIPTIONS, FALLTHROUGH_LABELS, Scope, StrictNeoScriptResolver, SPRITE_DERIVED_MEMBERS;
|
|
9347
|
+
var CONSTRUCTING_FUNCTION_KINDS, VARIANT_INITIALIZE_NAME, VARIANT_APPLY_NAME, MATH_NAMESPACE_NAME, MATH_FUNCTIONS, MATH_CONSTANTS, WRITE_TARGET_DESCRIPTIONS, FALLTHROUGH_LABELS, Scope, StrictNeoScriptResolver, SPRITE_DERIVED_MEMBERS;
|
|
9213
9348
|
var init_strict_resolver = __esm({
|
|
9214
9349
|
"../packages/neoscript-language/src/strict-resolver.ts"() {
|
|
9215
9350
|
"use strict";
|
|
@@ -9233,6 +9368,23 @@ var init_strict_resolver = __esm({
|
|
|
9233
9368
|
);
|
|
9234
9369
|
VARIANT_INITIALIZE_NAME = "Initialize";
|
|
9235
9370
|
VARIANT_APPLY_NAME = "ToVariant";
|
|
9371
|
+
MATH_NAMESPACE_NAME = "Math";
|
|
9372
|
+
MATH_FUNCTIONS = /* @__PURE__ */ new Map([
|
|
9373
|
+
["Min", { op: "min", arity: 2, result: "join" }],
|
|
9374
|
+
["Max", { op: "max", arity: 2, result: "join" }],
|
|
9375
|
+
["Clamp", { op: "clamp", arity: 3, result: "join" }],
|
|
9376
|
+
["Round", { op: "round", arity: 1, result: "integral" }],
|
|
9377
|
+
["Floor", { op: "floor", arity: 1, result: "integral" }],
|
|
9378
|
+
["Ceiling", { op: "ceiling", arity: 1, result: "integral" }],
|
|
9379
|
+
["Truncate", { op: "truncate", arity: 1, result: "integral" }],
|
|
9380
|
+
["Abs", { op: "abs", arity: 1, result: "join" }],
|
|
9381
|
+
["Sign", { op: "sign", arity: 1, result: "int" }],
|
|
9382
|
+
["Sqrt", { op: "sqrt", arity: 1, result: "float" }]
|
|
9383
|
+
]);
|
|
9384
|
+
MATH_CONSTANTS = /* @__PURE__ */ new Map([
|
|
9385
|
+
["PI", 3.141592653589793],
|
|
9386
|
+
["E", 2.718281828459045]
|
|
9387
|
+
]);
|
|
9236
9388
|
WRITE_TARGET_DESCRIPTIONS = {
|
|
9237
9389
|
this: "`this`",
|
|
9238
9390
|
local: "a local",
|
|
@@ -10230,6 +10382,12 @@ var init_strict_resolver = __esm({
|
|
|
10230
10382
|
};
|
|
10231
10383
|
}
|
|
10232
10384
|
assertLocalNameAvailable(name, scope, pos) {
|
|
10385
|
+
if (name === MATH_NAMESPACE_NAME) {
|
|
10386
|
+
throw new CompileError(
|
|
10387
|
+
`Cannot declare a local named '${MATH_NAMESPACE_NAME}' because it is a builtin namespace.`,
|
|
10388
|
+
pos
|
|
10389
|
+
);
|
|
10390
|
+
}
|
|
10233
10391
|
if (!scope.lookup(name)) return;
|
|
10234
10392
|
throw new CompileError(
|
|
10235
10393
|
`Cannot declare local '${name}' because that name is already used in an enclosing or current scope.`,
|
|
@@ -11447,6 +11605,12 @@ var init_strict_resolver = __esm({
|
|
|
11447
11605
|
false
|
|
11448
11606
|
);
|
|
11449
11607
|
}
|
|
11608
|
+
if (name === MATH_NAMESPACE_NAME) {
|
|
11609
|
+
throw new CompileError(
|
|
11610
|
+
`'${MATH_NAMESPACE_NAME}' is a builtin namespace, not a value.`,
|
|
11611
|
+
pos
|
|
11612
|
+
);
|
|
11613
|
+
}
|
|
11450
11614
|
const type = this.project.typeByName.get(name);
|
|
11451
11615
|
if (type?.kind === "enum") {
|
|
11452
11616
|
return {
|
|
@@ -11844,6 +12008,15 @@ var init_strict_resolver = __esm({
|
|
|
11844
12008
|
{ ...NEOSCRIPT_SPRITE_INFO_EMPTY_VALUE }
|
|
11845
12009
|
);
|
|
11846
12010
|
}
|
|
12011
|
+
if (receiverAst.name === MATH_NAMESPACE_NAME) {
|
|
12012
|
+
if (optional) {
|
|
12013
|
+
throw new CompileError(
|
|
12014
|
+
`Optional chaining is not valid on builtin namespace '${MATH_NAMESPACE_NAME}'.`,
|
|
12015
|
+
pos
|
|
12016
|
+
);
|
|
12017
|
+
}
|
|
12018
|
+
return this.resolveMathMemberRead(name, pos);
|
|
12019
|
+
}
|
|
11847
12020
|
const staticType = this.project.typeByName.get(receiverAst.name);
|
|
11848
12021
|
if (staticType && staticType.kind !== "enum") {
|
|
11849
12022
|
if (optional) {
|
|
@@ -12365,6 +12538,114 @@ var init_strict_resolver = __esm({
|
|
|
12365
12538
|
);
|
|
12366
12539
|
}
|
|
12367
12540
|
}
|
|
12541
|
+
/**
|
|
12542
|
+
* P69 §2.7. A member read on the `Math` namespace: the two constants lower
|
|
12543
|
+
* to `float` literals, and nothing else on `Math` is readable as a value.
|
|
12544
|
+
*/
|
|
12545
|
+
resolveMathMemberRead(name, pos) {
|
|
12546
|
+
const constant = MATH_CONSTANTS.get(name);
|
|
12547
|
+
if (constant !== void 0) {
|
|
12548
|
+
return literal({ kind: "primitive", name: "float" }, constant);
|
|
12549
|
+
}
|
|
12550
|
+
if (MATH_FUNCTIONS.has(name)) {
|
|
12551
|
+
throw new CompileError(
|
|
12552
|
+
`Math function '${MATH_NAMESPACE_NAME}.${name}' must be called with \`()\`; it cannot be read as a value.`,
|
|
12553
|
+
pos
|
|
12554
|
+
);
|
|
12555
|
+
}
|
|
12556
|
+
throw new CompileError(
|
|
12557
|
+
`${MATH_NAMESPACE_NAME} has no function '${name}'.`,
|
|
12558
|
+
pos
|
|
12559
|
+
);
|
|
12560
|
+
}
|
|
12561
|
+
/**
|
|
12562
|
+
* P69 §2. A call on the `Math` namespace. Every function is one symbol
|
|
12563
|
+
* whose result type is computed here from the resolved argument types —
|
|
12564
|
+
* the arithmetic operator join rule, verbatim — and lowers to a `mathOp`
|
|
12565
|
+
* intrinsic carrying the operands in declaration order.
|
|
12566
|
+
*/
|
|
12567
|
+
resolveMathCall(name, args, scope, pos) {
|
|
12568
|
+
const fn = MATH_FUNCTIONS.get(name);
|
|
12569
|
+
if (!fn) {
|
|
12570
|
+
if (MATH_CONSTANTS.has(name)) {
|
|
12571
|
+
throw new CompileError(
|
|
12572
|
+
`${MATH_NAMESPACE_NAME}.${name} is a constant, not a function; read it without \`()\`.`,
|
|
12573
|
+
pos
|
|
12574
|
+
);
|
|
12575
|
+
}
|
|
12576
|
+
throw new CompileError(
|
|
12577
|
+
`${MATH_NAMESPACE_NAME} has no function '${name}'.`,
|
|
12578
|
+
pos
|
|
12579
|
+
);
|
|
12580
|
+
}
|
|
12581
|
+
const label = `${MATH_NAMESPACE_NAME}.${name}`;
|
|
12582
|
+
requireArgCount(label, args, fn.arity, pos);
|
|
12583
|
+
const resolved = args.map((argument2, index) => {
|
|
12584
|
+
const value = this.resolveExpression(argument2, scope);
|
|
12585
|
+
if (isNullable(value.type)) {
|
|
12586
|
+
throw new CompileError(
|
|
12587
|
+
`${label} requires non-optional arguments; got ${this.describe(value.type)} for argument ${index + 1}.`,
|
|
12588
|
+
argument2.pos
|
|
12589
|
+
);
|
|
12590
|
+
}
|
|
12591
|
+
if (!isNumeric(value.type)) {
|
|
12592
|
+
throw new CompileError(
|
|
12593
|
+
`${label} requires numeric arguments; got ${this.describe(value.type)} for argument ${index + 1}.`,
|
|
12594
|
+
argument2.pos
|
|
12595
|
+
);
|
|
12596
|
+
}
|
|
12597
|
+
return value;
|
|
12598
|
+
});
|
|
12599
|
+
const joined = this.joinMathArguments(args, resolved, scope, pos);
|
|
12600
|
+
return intrinsic(
|
|
12601
|
+
"mathOp" /* MathOp */,
|
|
12602
|
+
{
|
|
12603
|
+
op: fn.op,
|
|
12604
|
+
argPointers: joined.arguments.map((argument2) => argument2.pointer),
|
|
12605
|
+
...isPrimitive(joined.type, "decimal") ? { decimal: true } : {}
|
|
12606
|
+
},
|
|
12607
|
+
mathResultType(fn.result, joined.type, pos)
|
|
12608
|
+
);
|
|
12609
|
+
}
|
|
12610
|
+
/**
|
|
12611
|
+
* P69 §2.1. The join of a `Math` call's argument types, after the second
|
|
12612
|
+
* resolution pass that gives numeric literals beside a `decimal` argument
|
|
12613
|
+
* their decimal reading.
|
|
12614
|
+
*
|
|
12615
|
+
* A call's argument list carries no declared expected type once typing is
|
|
12616
|
+
* inferred, so `Math.Max(price, 0.99)` would otherwise type `0.99` as
|
|
12617
|
+
* `float` and die on the mix rule with no decimal literal suffix to escape
|
|
12618
|
+
* with. Re-resolving from the preserved raw lexeme is the assignment-context
|
|
12619
|
+
* rule applied across the argument list, not a new coercion: a non-literal
|
|
12620
|
+
* `float` expression still refuses to mix.
|
|
12621
|
+
*/
|
|
12622
|
+
joinMathArguments(args, resolved, scope, pos) {
|
|
12623
|
+
const decimalType = {
|
|
12624
|
+
kind: "primitive",
|
|
12625
|
+
name: "decimal"
|
|
12626
|
+
};
|
|
12627
|
+
const anyDecimal = resolved.some(
|
|
12628
|
+
(argument2) => isPrimitive(argument2.type, "decimal")
|
|
12629
|
+
);
|
|
12630
|
+
const settled = !anyDecimal ? resolved : resolved.map((argument2, index) => {
|
|
12631
|
+
const ast = requiredAt(args, index);
|
|
12632
|
+
if (!isContextualNumericLiteral(ast)) return argument2;
|
|
12633
|
+
return this.resolveExpression(ast, scope, decimalType);
|
|
12634
|
+
});
|
|
12635
|
+
if (anyDecimal && settled.some((argument2) => isPrimitive(argument2.type, "float"))) {
|
|
12636
|
+
throw new CompileError(
|
|
12637
|
+
"Decimal and Float cannot be mixed implicitly; convert explicitly with value.ToDecimal(digits) or value.ToFloat().",
|
|
12638
|
+
pos
|
|
12639
|
+
);
|
|
12640
|
+
}
|
|
12641
|
+
return {
|
|
12642
|
+
arguments: settled,
|
|
12643
|
+
type: settled.reduce(
|
|
12644
|
+
(left, right) => numericResult(left, right.type),
|
|
12645
|
+
{ kind: "primitive", name: "int" }
|
|
12646
|
+
)
|
|
12647
|
+
};
|
|
12648
|
+
}
|
|
12368
12649
|
resolveCall(callee, argumentsList2, scope, pos) {
|
|
12369
12650
|
const variantCall = this.resolveVariantCall(
|
|
12370
12651
|
callee,
|
|
@@ -12458,6 +12739,15 @@ var init_strict_resolver = __esm({
|
|
|
12458
12739
|
);
|
|
12459
12740
|
}
|
|
12460
12741
|
if (callee.receiver.kind === "ident" && !scope.lookup(callee.receiver.name)) {
|
|
12742
|
+
if (callee.receiver.name === MATH_NAMESPACE_NAME) {
|
|
12743
|
+
if (callee.optional) {
|
|
12744
|
+
throw new CompileError(
|
|
12745
|
+
`Optional chaining is not valid on builtin namespace '${MATH_NAMESPACE_NAME}'.`,
|
|
12746
|
+
pos
|
|
12747
|
+
);
|
|
12748
|
+
}
|
|
12749
|
+
return this.resolveMathCall(callee.name, argumentsList2, scope, pos);
|
|
12750
|
+
}
|
|
12461
12751
|
const staticType = this.project.typeByName.get(callee.receiver.name);
|
|
12462
12752
|
if (staticType && staticType.kind !== "enum") {
|
|
12463
12753
|
if (callee.optional) {
|
|
@@ -14318,6 +14608,12 @@ var init_strict_resolver = __esm({
|
|
|
14318
14608
|
resolved = { kind: "primitive", name: type.name };
|
|
14319
14609
|
break;
|
|
14320
14610
|
case "named": {
|
|
14611
|
+
if (type.name === MATH_NAMESPACE_NAME) {
|
|
14612
|
+
throw new CompileError(
|
|
14613
|
+
`'${MATH_NAMESPACE_NAME}' is a builtin namespace, not a type.`,
|
|
14614
|
+
type.pos
|
|
14615
|
+
);
|
|
14616
|
+
}
|
|
14321
14617
|
const builtin = namedPrimitive(type.name);
|
|
14322
14618
|
if (builtin) {
|
|
14323
14619
|
resolved = { kind: "primitive", name: builtin };
|
|
@@ -26195,7 +26491,7 @@ var init_project_source_type_checker = __esm({
|
|
|
26195
26491
|
]);
|
|
26196
26492
|
BUILTIN_TYPES = /* @__PURE__ */ new Set([
|
|
26197
26493
|
...NEOSCRIPT_PRIMITIVE_TYPES,
|
|
26198
|
-
...
|
|
26494
|
+
...NEOSCRIPT_BUILTIN_ANNOTATION_TYPES,
|
|
26199
26495
|
"void",
|
|
26200
26496
|
"null",
|
|
26201
26497
|
"Project",
|
|
@@ -26213,7 +26509,10 @@ var init_project_source_type_checker = __esm({
|
|
|
26213
26509
|
"NeoAudioClip",
|
|
26214
26510
|
"ProjectRelation"
|
|
26215
26511
|
]);
|
|
26216
|
-
RESERVED_NAMES2 = new Set(
|
|
26512
|
+
RESERVED_NAMES2 = /* @__PURE__ */ new Set([
|
|
26513
|
+
...NEOSCRIPT_KEYWORDS,
|
|
26514
|
+
...NEOSCRIPT_BUILTIN_NAMESPACES
|
|
26515
|
+
]);
|
|
26217
26516
|
}
|
|
26218
26517
|
});
|
|
26219
26518
|
|
|
@@ -27116,7 +27415,7 @@ var init_project_source_analysis = __esm({
|
|
|
27116
27415
|
init_project_source_parameter_defaults();
|
|
27117
27416
|
BUILTIN_TYPE_NAMES = /* @__PURE__ */ new Set([
|
|
27118
27417
|
...NEOSCRIPT_PRIMITIVE_TYPES,
|
|
27119
|
-
...
|
|
27418
|
+
...NEOSCRIPT_BUILTIN_ANNOTATION_TYPES,
|
|
27120
27419
|
"void",
|
|
27121
27420
|
"null",
|
|
27122
27421
|
"Project",
|
|
@@ -27134,7 +27433,10 @@ var init_project_source_analysis = __esm({
|
|
|
27134
27433
|
"NeoAudioClip",
|
|
27135
27434
|
"ProjectRelation"
|
|
27136
27435
|
]);
|
|
27137
|
-
RESERVED_NAMES3 = new Set(
|
|
27436
|
+
RESERVED_NAMES3 = /* @__PURE__ */ new Set([
|
|
27437
|
+
...NEOSCRIPT_KEYWORDS,
|
|
27438
|
+
...NEOSCRIPT_BUILTIN_NAMESPACES
|
|
27439
|
+
]);
|
|
27138
27440
|
SYSTEM_RECORD_ID_PREFIX = "system_";
|
|
27139
27441
|
RFC_4122_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
|
|
27140
27442
|
QUARANTINED_LEGACY_SYSTEM_ID_PREFIXES = [
|
|
@@ -27634,7 +27936,7 @@ function projectFallbackCompletions(analysis, document, position) {
|
|
|
27634
27936
|
}
|
|
27635
27937
|
for (const typeName of [
|
|
27636
27938
|
...NEOSCRIPT_PRIMITIVE_TYPES,
|
|
27637
|
-
...
|
|
27939
|
+
...NEOSCRIPT_BUILTIN_ANNOTATION_TYPES
|
|
27638
27940
|
]) {
|
|
27639
27941
|
if (expression) continue;
|
|
27640
27942
|
items.set(typeName, {
|
|
@@ -41565,6 +41867,18 @@ function isNSFunctionDecimalOp(value) {
|
|
|
41565
41867
|
const digits = info.digitsPointer;
|
|
41566
41868
|
return digits === void 0 || isNSPointer(digits);
|
|
41567
41869
|
}
|
|
41870
|
+
function isNSFunctionMathOp(value) {
|
|
41871
|
+
const v = value;
|
|
41872
|
+
if (v?.type !== "mathOp" /* mathOp */) return false;
|
|
41873
|
+
const info = v.info;
|
|
41874
|
+
if (typeof info !== "object" || info === null) return false;
|
|
41875
|
+
if (!NS_MATH_OPS.includes(info.op)) return false;
|
|
41876
|
+
const args = info.argPointers;
|
|
41877
|
+
if (!Array.isArray(args)) return false;
|
|
41878
|
+
if (!args.every(isNSPointer)) return false;
|
|
41879
|
+
const decimal2 = info.decimal;
|
|
41880
|
+
return decimal2 === void 0 || decimal2 === true;
|
|
41881
|
+
}
|
|
41568
41882
|
function isNSFunctionCount(value) {
|
|
41569
41883
|
const v = value;
|
|
41570
41884
|
if (v?.type !== "count" /* count */) return false;
|
|
@@ -41612,7 +41926,7 @@ function isNSFunction(value) {
|
|
|
41612
41926
|
return isNSFunctionClassConstructor(value) || isNSFunctionClassClone(value) || isNSFunctionListIndex(value) || isNSFunctionSelect(value) || isNSFunctionFirst(value) || isNSFunctionFirstOrDefault(value) || isNSFunctionWhere(value) || isNSFunctionContains(value) || // stringOp was missing from this chain since its introduction — a
|
|
41613
41927
|
// compiled getter using ToLower()/StartsWith() would fail pointer
|
|
41614
41928
|
// validation anywhere isNSFunction gates. Fixed alongside decimalOp.
|
|
41615
|
-
isNSFunctionStringOp(value) || isNSFunctionDecimalOp(value) || isNSFunctionCount(value) || isNSFunctionVisitCount(value) || isNSFunctionHasVisited(value) || isNSFunctionVectorConstructor(value) || isNSFunctionImageSlice(value) || isNSFunctionVariantInitialize(value) || isNSFunctionVariantApply(value) || isNSFunctionDeclaredConstructor(value);
|
|
41929
|
+
isNSFunctionStringOp(value) || isNSFunctionDecimalOp(value) || isNSFunctionMathOp(value) || isNSFunctionCount(value) || isNSFunctionVisitCount(value) || isNSFunctionHasVisited(value) || isNSFunctionVectorConstructor(value) || isNSFunctionImageSlice(value) || isNSFunctionVariantInitialize(value) || isNSFunctionVariantApply(value) || isNSFunctionDeclaredConstructor(value);
|
|
41616
41930
|
}
|
|
41617
41931
|
function isNSInstructionVariable(value) {
|
|
41618
41932
|
const v = value;
|
|
@@ -41903,7 +42217,7 @@ function isNSVoidBody(value) {
|
|
|
41903
42217
|
if (value.typeInfo.type !== 0 /* Null */) return false;
|
|
41904
42218
|
return value.typeInfo.required === true;
|
|
41905
42219
|
}
|
|
41906
|
-
var NS_STRING_OPS, NS_DECIMAL_OPS;
|
|
42220
|
+
var NS_STRING_OPS, NS_DECIMAL_OPS, NS_MATH_OPS;
|
|
41907
42221
|
var init_neoscript_guards = __esm({
|
|
41908
42222
|
"../src/models/neoscript/neoscript-guards.ts"() {
|
|
41909
42223
|
"use strict";
|
|
@@ -41924,6 +42238,18 @@ var init_neoscript_guards = __esm({
|
|
|
41924
42238
|
"toFloat",
|
|
41925
42239
|
"toDecimal"
|
|
41926
42240
|
];
|
|
42241
|
+
NS_MATH_OPS = [
|
|
42242
|
+
"min",
|
|
42243
|
+
"max",
|
|
42244
|
+
"clamp",
|
|
42245
|
+
"round",
|
|
42246
|
+
"floor",
|
|
42247
|
+
"ceiling",
|
|
42248
|
+
"truncate",
|
|
42249
|
+
"abs",
|
|
42250
|
+
"sign",
|
|
42251
|
+
"sqrt"
|
|
42252
|
+
];
|
|
41927
42253
|
}
|
|
41928
42254
|
});
|
|
41929
42255
|
|
|
@@ -63087,6 +63413,25 @@ function compareDecimalStrings(a, b) {
|
|
|
63087
63413
|
if (aligned.a > aligned.b) return 1;
|
|
63088
63414
|
return 0;
|
|
63089
63415
|
}
|
|
63416
|
+
function minDecimalStrings(a, b) {
|
|
63417
|
+
return compareDecimalStrings(a, b) <= 0 ? a : b;
|
|
63418
|
+
}
|
|
63419
|
+
function maxDecimalStrings(a, b) {
|
|
63420
|
+
return compareDecimalStrings(a, b) >= 0 ? a : b;
|
|
63421
|
+
}
|
|
63422
|
+
function clampDecimalStrings(value, min, max) {
|
|
63423
|
+
if (compareDecimalStrings(min, max) > 0) {
|
|
63424
|
+
throw new DecimalClampRangeError(MATH_CLAMP_RANGE_MESSAGE);
|
|
63425
|
+
}
|
|
63426
|
+
if (compareDecimalStrings(value, min) < 0) return min;
|
|
63427
|
+
if (compareDecimalStrings(value, max) > 0) return max;
|
|
63428
|
+
return value;
|
|
63429
|
+
}
|
|
63430
|
+
function absDecimalString(value) {
|
|
63431
|
+
const parts = parseDecimalArg(value, "value");
|
|
63432
|
+
const magnitude = parts.coefficient < 0n ? -parts.coefficient : parts.coefficient;
|
|
63433
|
+
return formatDecimalParts({ coefficient: magnitude, scale: parts.scale });
|
|
63434
|
+
}
|
|
63090
63435
|
function addDecimalStrings(a, b) {
|
|
63091
63436
|
const aligned = alignScales(parseDecimalArg(a, "a"), parseDecimalArg(b, "b"));
|
|
63092
63437
|
return formatDecimalParts(
|
|
@@ -63149,6 +63494,32 @@ function roundDecimalString(value, digits) {
|
|
|
63149
63494
|
const rounded = roundParts(parseDecimalArg(value, "value"), digits);
|
|
63150
63495
|
return formatDecimalParts(assertWithinEnvelope(rounded, "round"));
|
|
63151
63496
|
}
|
|
63497
|
+
function toIntegralParts(parts, direction) {
|
|
63498
|
+
if (parts.scale === 0) return parts;
|
|
63499
|
+
const divisor = pow10(parts.scale);
|
|
63500
|
+
const quotient = parts.coefficient / divisor;
|
|
63501
|
+
const remainder = parts.coefficient % divisor;
|
|
63502
|
+
if (remainder === 0n) return { coefficient: quotient, scale: 0 };
|
|
63503
|
+
if (direction === "floor" && remainder < 0n) {
|
|
63504
|
+
return { coefficient: quotient - 1n, scale: 0 };
|
|
63505
|
+
}
|
|
63506
|
+
if (direction === "ceiling" && remainder > 0n) {
|
|
63507
|
+
return { coefficient: quotient + 1n, scale: 0 };
|
|
63508
|
+
}
|
|
63509
|
+
return { coefficient: quotient, scale: 0 };
|
|
63510
|
+
}
|
|
63511
|
+
function floorDecimalString(value) {
|
|
63512
|
+
const parts = toIntegralParts(parseDecimalArg(value, "value"), "floor");
|
|
63513
|
+
return formatDecimalParts(assertWithinEnvelope(parts, "floor"));
|
|
63514
|
+
}
|
|
63515
|
+
function ceilingDecimalString(value) {
|
|
63516
|
+
const parts = toIntegralParts(parseDecimalArg(value, "value"), "ceiling");
|
|
63517
|
+
return formatDecimalParts(assertWithinEnvelope(parts, "ceiling"));
|
|
63518
|
+
}
|
|
63519
|
+
function truncateDecimalString(value) {
|
|
63520
|
+
const parts = toIntegralParts(parseDecimalArg(value, "value"), "truncate");
|
|
63521
|
+
return formatDecimalParts(assertWithinEnvelope(parts, "truncate"));
|
|
63522
|
+
}
|
|
63152
63523
|
function divideDecimalStrings(a, b, digits) {
|
|
63153
63524
|
assertDigitsInRange(digits, "divide");
|
|
63154
63525
|
const dividend = parseDecimalArg(a, "a");
|
|
@@ -63229,7 +63600,7 @@ function floatFromDecimalString(value) {
|
|
|
63229
63600
|
assertCanonicalInput(value, "value");
|
|
63230
63601
|
return Number(value);
|
|
63231
63602
|
}
|
|
63232
|
-
var DecimalOverflowError, DecimalDivisionByZeroError, DecimalDigitsRangeError, DecimalNonFiniteError, TEN;
|
|
63603
|
+
var DecimalOverflowError, DecimalDivisionByZeroError, DecimalDigitsRangeError, DecimalNonFiniteError, DecimalClampRangeError, MATH_CLAMP_RANGE_MESSAGE, TEN;
|
|
63233
63604
|
var init_decimal_math = __esm({
|
|
63234
63605
|
"../src/models/decimal/decimal-math.ts"() {
|
|
63235
63606
|
"use strict";
|
|
@@ -63242,6 +63613,9 @@ var init_decimal_math = __esm({
|
|
|
63242
63613
|
};
|
|
63243
63614
|
DecimalNonFiniteError = class extends Error {
|
|
63244
63615
|
};
|
|
63616
|
+
DecimalClampRangeError = class extends Error {
|
|
63617
|
+
};
|
|
63618
|
+
MATH_CLAMP_RANGE_MESSAGE = "Math.Clamp requires min <= max.";
|
|
63245
63619
|
TEN = 10n;
|
|
63246
63620
|
}
|
|
63247
63621
|
});
|
|
@@ -68130,7 +68504,7 @@ function coerceDecimalOperand(value, context) {
|
|
|
68130
68504
|
);
|
|
68131
68505
|
}
|
|
68132
68506
|
function rethrowDecimalError(error) {
|
|
68133
|
-
if (error instanceof DecimalOverflowError || error instanceof DecimalDivisionByZeroError || error instanceof DecimalDigitsRangeError || error instanceof DecimalNonFiniteError) {
|
|
68507
|
+
if (error instanceof DecimalOverflowError || error instanceof DecimalDivisionByZeroError || error instanceof DecimalDigitsRangeError || error instanceof DecimalNonFiniteError || error instanceof DecimalClampRangeError) {
|
|
68134
68508
|
throw new NSGetterRuntimeError(error.message);
|
|
68135
68509
|
}
|
|
68136
68510
|
throw error;
|
|
@@ -68157,6 +68531,163 @@ function applyDecimalArithmetic(op, operands) {
|
|
|
68157
68531
|
rethrowDecimalError(error);
|
|
68158
68532
|
}
|
|
68159
68533
|
}
|
|
68534
|
+
function mathArgPointer(info, index, fn) {
|
|
68535
|
+
const pointer = info.argPointers[index];
|
|
68536
|
+
if (pointer === void 0) {
|
|
68537
|
+
throw new NSGetterRuntimeError(
|
|
68538
|
+
`Math.${fn} is missing argument ${index + 1}.`
|
|
68539
|
+
);
|
|
68540
|
+
}
|
|
68541
|
+
return pointer;
|
|
68542
|
+
}
|
|
68543
|
+
function mathNumberArg(value, fn) {
|
|
68544
|
+
if (value === null || value === void 0) {
|
|
68545
|
+
throw new NSGetterRuntimeError(`Math.${fn} argument is null.`);
|
|
68546
|
+
}
|
|
68547
|
+
if (typeof value !== "number") {
|
|
68548
|
+
throw new NSGetterRuntimeError(
|
|
68549
|
+
`Math.${fn} argument is not numeric: ${typeof value}.`
|
|
68550
|
+
);
|
|
68551
|
+
}
|
|
68552
|
+
return value;
|
|
68553
|
+
}
|
|
68554
|
+
function mathDecimalArg(value, fn) {
|
|
68555
|
+
if (value === null || value === void 0) {
|
|
68556
|
+
throw new NSGetterRuntimeError(`Math.${fn} argument is null.`);
|
|
68557
|
+
}
|
|
68558
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
68559
|
+
throw new NSGetterRuntimeError(
|
|
68560
|
+
`Math.${fn} argument is not numeric: ${typeof value}.`
|
|
68561
|
+
);
|
|
68562
|
+
}
|
|
68563
|
+
return coerceDecimalOperand(value, `Math.${fn}`);
|
|
68564
|
+
}
|
|
68565
|
+
function requireFiniteMathArg(value, fn) {
|
|
68566
|
+
if (!Number.isFinite(value)) {
|
|
68567
|
+
throw new NSGetterRuntimeError(`Math.${fn} requires a finite argument.`);
|
|
68568
|
+
}
|
|
68569
|
+
return value;
|
|
68570
|
+
}
|
|
68571
|
+
function roundHalfEven(value) {
|
|
68572
|
+
if (Math.abs(value) >= DOUBLE_INTEGRAL_MAGNITUDE) return value;
|
|
68573
|
+
const integral = Math.floor(value);
|
|
68574
|
+
const fraction = value - integral;
|
|
68575
|
+
if (fraction > 0.5) return integral + 1;
|
|
68576
|
+
if (fraction < 0.5) return integral;
|
|
68577
|
+
if (integral % 2 === 0) return integral;
|
|
68578
|
+
return integral + 1;
|
|
68579
|
+
}
|
|
68580
|
+
function evalMathOp(info, scope, ctx) {
|
|
68581
|
+
const fn = NS_MATH_OP_NAMES[info.op];
|
|
68582
|
+
if (info.decimal === true) return evalDecimalMathOp(info, fn, scope, ctx);
|
|
68583
|
+
const value = mathNumberArg(
|
|
68584
|
+
evalPointer(mathArgPointer(info, 0, fn), scope, ctx),
|
|
68585
|
+
fn
|
|
68586
|
+
);
|
|
68587
|
+
switch (info.op) {
|
|
68588
|
+
case "min":
|
|
68589
|
+
return Math.min(
|
|
68590
|
+
value,
|
|
68591
|
+
mathNumberArg(evalPointer(mathArgPointer(info, 1, fn), scope, ctx), fn)
|
|
68592
|
+
);
|
|
68593
|
+
case "max":
|
|
68594
|
+
return Math.max(
|
|
68595
|
+
value,
|
|
68596
|
+
mathNumberArg(evalPointer(mathArgPointer(info, 1, fn), scope, ctx), fn)
|
|
68597
|
+
);
|
|
68598
|
+
case "clamp": {
|
|
68599
|
+
const min = mathNumberArg(
|
|
68600
|
+
evalPointer(mathArgPointer(info, 1, fn), scope, ctx),
|
|
68601
|
+
fn
|
|
68602
|
+
);
|
|
68603
|
+
const max = mathNumberArg(
|
|
68604
|
+
evalPointer(mathArgPointer(info, 2, fn), scope, ctx),
|
|
68605
|
+
fn
|
|
68606
|
+
);
|
|
68607
|
+
if (min > max) {
|
|
68608
|
+
throw new NSGetterRuntimeError(MATH_CLAMP_RANGE_MESSAGE);
|
|
68609
|
+
}
|
|
68610
|
+
if (value < min) return min;
|
|
68611
|
+
if (value > max) return max;
|
|
68612
|
+
return value;
|
|
68613
|
+
}
|
|
68614
|
+
case "round":
|
|
68615
|
+
return roundHalfEven(requireFiniteMathArg(value, fn));
|
|
68616
|
+
case "floor":
|
|
68617
|
+
return Math.floor(requireFiniteMathArg(value, fn));
|
|
68618
|
+
case "ceiling":
|
|
68619
|
+
return Math.ceil(requireFiniteMathArg(value, fn));
|
|
68620
|
+
case "truncate":
|
|
68621
|
+
return Math.trunc(requireFiniteMathArg(value, fn));
|
|
68622
|
+
case "abs":
|
|
68623
|
+
return Math.abs(value);
|
|
68624
|
+
case "sign":
|
|
68625
|
+
if (Number.isNaN(value)) {
|
|
68626
|
+
throw new NSGetterRuntimeError("Math.Sign is undefined for NaN.");
|
|
68627
|
+
}
|
|
68628
|
+
if (value > 0) return 1;
|
|
68629
|
+
if (value < 0) return -1;
|
|
68630
|
+
return 0;
|
|
68631
|
+
case "sqrt":
|
|
68632
|
+
return Math.sqrt(value);
|
|
68633
|
+
}
|
|
68634
|
+
}
|
|
68635
|
+
function evalDecimalMathOp(info, fn, scope, ctx) {
|
|
68636
|
+
const value = mathDecimalArg(
|
|
68637
|
+
evalPointer(mathArgPointer(info, 0, fn), scope, ctx),
|
|
68638
|
+
fn
|
|
68639
|
+
);
|
|
68640
|
+
try {
|
|
68641
|
+
switch (info.op) {
|
|
68642
|
+
case "min":
|
|
68643
|
+
return minDecimalStrings(
|
|
68644
|
+
value,
|
|
68645
|
+
mathDecimalArg(
|
|
68646
|
+
evalPointer(mathArgPointer(info, 1, fn), scope, ctx),
|
|
68647
|
+
fn
|
|
68648
|
+
)
|
|
68649
|
+
);
|
|
68650
|
+
case "max":
|
|
68651
|
+
return maxDecimalStrings(
|
|
68652
|
+
value,
|
|
68653
|
+
mathDecimalArg(
|
|
68654
|
+
evalPointer(mathArgPointer(info, 1, fn), scope, ctx),
|
|
68655
|
+
fn
|
|
68656
|
+
)
|
|
68657
|
+
);
|
|
68658
|
+
case "clamp":
|
|
68659
|
+
return clampDecimalStrings(
|
|
68660
|
+
value,
|
|
68661
|
+
mathDecimalArg(
|
|
68662
|
+
evalPointer(mathArgPointer(info, 1, fn), scope, ctx),
|
|
68663
|
+
fn
|
|
68664
|
+
),
|
|
68665
|
+
mathDecimalArg(
|
|
68666
|
+
evalPointer(mathArgPointer(info, 2, fn), scope, ctx),
|
|
68667
|
+
fn
|
|
68668
|
+
)
|
|
68669
|
+
);
|
|
68670
|
+
case "round":
|
|
68671
|
+
return roundDecimalString(value, 0);
|
|
68672
|
+
case "floor":
|
|
68673
|
+
return floorDecimalString(value);
|
|
68674
|
+
case "ceiling":
|
|
68675
|
+
return ceilingDecimalString(value);
|
|
68676
|
+
case "truncate":
|
|
68677
|
+
return truncateDecimalString(value);
|
|
68678
|
+
case "abs":
|
|
68679
|
+
return absDecimalString(value);
|
|
68680
|
+
case "sign":
|
|
68681
|
+
return compareDecimalStrings(value, "0");
|
|
68682
|
+
case "sqrt":
|
|
68683
|
+
throw new NSGetterRuntimeError(
|
|
68684
|
+
"Math.Sqrt does not accept decimal arguments."
|
|
68685
|
+
);
|
|
68686
|
+
}
|
|
68687
|
+
} catch (error) {
|
|
68688
|
+
rethrowDecimalError(error);
|
|
68689
|
+
}
|
|
68690
|
+
}
|
|
68160
68691
|
function stringifyForInterp(v) {
|
|
68161
68692
|
if (v === null || v === void 0) return "";
|
|
68162
68693
|
if (typeof v === "string") return v;
|
|
@@ -68676,6 +69207,8 @@ function evalFunction(fn, scope, ctx) {
|
|
|
68676
69207
|
}
|
|
68677
69208
|
break;
|
|
68678
69209
|
}
|
|
69210
|
+
case "mathOp" /* mathOp */:
|
|
69211
|
+
return evalMathOp(fn.info, scope, ctx);
|
|
68679
69212
|
case "stringOp" /* stringOp */: {
|
|
68680
69213
|
const receiver = evalPointer(fn.info.receiverPointer, scope, ctx);
|
|
68681
69214
|
if (typeof receiver !== "string") {
|
|
@@ -71888,7 +72421,7 @@ function iterateCollection(c, ctx, callback) {
|
|
|
71888
72421
|
return callback(entry, key, valueId);
|
|
71889
72422
|
});
|
|
71890
72423
|
}
|
|
71891
|
-
var DELEGATE_LEXICAL_THIS, DELEGATE_LEXICAL_ROOT, NonCatchableNSGetterRuntimeError, NativeFunctionDelegateUnavailableError, CorruptNeoScriptIRError, NeoScriptResourceLimitError, NeoScriptWallClockTimeoutError, DEFAULT_NEO_SCRIPT_EXECUTION_BUDGET_LIMITS, liveListIndexesByProject, evaluatorOwnershipCachesByBase, MAX_CONSTRUCTION_DEPTH, MAX_LOOP_ITERATIONS, resolutionCacheByMembers, NO_SCHEMA_REVISION, LazyValueOverlay, READONLY_FOREACH_BINDING_ERROR, READONLY_CATCH_BINDING_ERROR, EMPTY_SUPPLIED_CONSTRUCTOR_FIELDS;
|
|
72424
|
+
var DELEGATE_LEXICAL_THIS, DELEGATE_LEXICAL_ROOT, NonCatchableNSGetterRuntimeError, NativeFunctionDelegateUnavailableError, CorruptNeoScriptIRError, NeoScriptResourceLimitError, NeoScriptWallClockTimeoutError, DEFAULT_NEO_SCRIPT_EXECUTION_BUDGET_LIMITS, liveListIndexesByProject, evaluatorOwnershipCachesByBase, MAX_CONSTRUCTION_DEPTH, MAX_LOOP_ITERATIONS, resolutionCacheByMembers, NO_SCHEMA_REVISION, LazyValueOverlay, READONLY_FOREACH_BINDING_ERROR, READONLY_CATCH_BINDING_ERROR, NS_MATH_OP_NAMES, DOUBLE_INTEGRAL_MAGNITUDE, EMPTY_SUPPLIED_CONSTRUCTOR_FIELDS;
|
|
71892
72425
|
var init_evaluateNSGetter = __esm({
|
|
71893
72426
|
"../src/view-models/neoscript-evaluator/evaluateNSGetter.ts"() {
|
|
71894
72427
|
"use strict";
|
|
@@ -72032,6 +72565,19 @@ var init_evaluateNSGetter = __esm({
|
|
|
72032
72565
|
};
|
|
72033
72566
|
READONLY_FOREACH_BINDING_ERROR = "Cannot assign to a read-only foreach iterator binding.";
|
|
72034
72567
|
READONLY_CATCH_BINDING_ERROR = "Cannot assign to a read-only catch message binding.";
|
|
72568
|
+
NS_MATH_OP_NAMES = {
|
|
72569
|
+
min: "Min",
|
|
72570
|
+
max: "Max",
|
|
72571
|
+
clamp: "Clamp",
|
|
72572
|
+
round: "Round",
|
|
72573
|
+
floor: "Floor",
|
|
72574
|
+
ceiling: "Ceiling",
|
|
72575
|
+
truncate: "Truncate",
|
|
72576
|
+
abs: "Abs",
|
|
72577
|
+
sign: "Sign",
|
|
72578
|
+
sqrt: "Sqrt"
|
|
72579
|
+
};
|
|
72580
|
+
DOUBLE_INTEGRAL_MAGNITUDE = 2 ** 53;
|
|
72035
72581
|
EMPTY_SUPPLIED_CONSTRUCTOR_FIELDS = [];
|
|
72036
72582
|
}
|
|
72037
72583
|
});
|
|
@@ -111706,7 +112252,7 @@ var init_registry2 = __esm({
|
|
|
111706
112252
|
PROJECT_SCHEMA_CONTRACT = Object.freeze({
|
|
111707
112253
|
formatVersion: 3,
|
|
111708
112254
|
contractVersion: "3.13",
|
|
111709
|
-
cliVersion: "0.
|
|
112255
|
+
cliVersion: "0.32.0",
|
|
111710
112256
|
projectFileUploadBatchSize: 32,
|
|
111711
112257
|
documentRecords: {
|
|
111712
112258
|
member: {
|
|
@@ -118301,7 +118847,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
|
|
|
118301
118847
|
async function main() {
|
|
118302
118848
|
const args = parseArgs(process.argv.slice(2));
|
|
118303
118849
|
if (args.command === "--version") {
|
|
118304
|
-
console.log("0.
|
|
118850
|
+
console.log("0.32.0");
|
|
118305
118851
|
return;
|
|
118306
118852
|
}
|
|
118307
118853
|
if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ description: >-
|
|
|
9
9
|
`@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
<!-- reviewed-through-cli: 0.
|
|
12
|
+
<!-- reviewed-through-cli: 0.32.0 -->
|
|
13
13
|
|
|
14
14
|
# Neo Compose CLI
|
|
15
15
|
|
|
@@ -138,7 +138,8 @@ completely before editing:
|
|
|
138
138
|
variant folders, partial animation overrides, segments, tracks, and NeoFlow
|
|
139
139
|
dialogues.
|
|
140
140
|
- [NeoScript](references/neoscript.md): inline bodies, snippets, nullability,
|
|
141
|
-
loops, switch, try/catch, ownership, evaluation, and
|
|
141
|
+
numeric builtins, loops, switch, try/catch, ownership, evaluation, and
|
|
142
|
+
migrations.
|
|
142
143
|
- [Commands and synchronization](references/commands-and-sync.md): pull/push,
|
|
143
144
|
conflicts, low-level repair, branches, releases, and history.
|
|
144
145
|
- [CLI development](references/cli-development.md): implementation source of
|
|
@@ -83,7 +83,7 @@ wrappers.
|
|
|
83
83
|
The marker near the top of `SKILL.md` must exactly match the package version:
|
|
84
84
|
|
|
85
85
|
```html
|
|
86
|
-
<!-- reviewed-through-cli: 0.
|
|
86
|
+
<!-- reviewed-through-cli: 0.32.0 -->
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
The quoted version above is checked too, so this instruction cannot go stale
|
|
@@ -7,6 +7,7 @@ runtime ownership, evaluation, and migrations.
|
|
|
7
7
|
|
|
8
8
|
- Inline bodies and snippets
|
|
9
9
|
- Nullability and ownership
|
|
10
|
+
- Numeric builtins
|
|
10
11
|
- Logical operators
|
|
11
12
|
- Loops and transfer
|
|
12
13
|
- Switch
|
|
@@ -81,6 +82,27 @@ Save, Session, Setter, or otherwise writable. A constructor writes only its
|
|
|
81
82
|
`this` instance. A writable structured-leaf field still inherits the receiver's
|
|
82
83
|
effective storage restrictions.
|
|
83
84
|
|
|
85
|
+
## Numeric builtins
|
|
86
|
+
|
|
87
|
+
Use the `Math` namespace for numeric utilities with C# names and C# semantics:
|
|
88
|
+
`Min`, `Max`, `Clamp`, `Round`, `Floor`, `Ceiling`, `Truncate`, `Abs`, `Sign`,
|
|
89
|
+
`Sqrt`, and the `PI` and `E` constants.
|
|
90
|
+
|
|
91
|
+
```neo
|
|
92
|
+
return Math.Clamp(this.Quality + bonus, 0, 10);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Each result follows the arithmetic join of its argument types: all-`int` gives
|
|
96
|
+
`int`, any `float` gives `float`, any `decimal` gives `decimal`, and `decimal`
|
|
97
|
+
never mixes with `float`. `Round`, `Floor`, `Ceiling`, and `Truncate` are the
|
|
98
|
+
language's `float`-to-`int` conversions: they return `int` for `int` and
|
|
99
|
+
`float` input, keep a `decimal` a `decimal`, and reject a non-finite argument
|
|
100
|
+
at runtime. `Round` rounds midpoints to even, matching `decimal.Round(0)`.
|
|
101
|
+
`Sign` always returns `int`; `Sqrt` takes `int` or `float` only.
|
|
102
|
+
|
|
103
|
+
`Math` is a qualifier, never a type or a value, and the name is reserved: no
|
|
104
|
+
Class, enum, member, parameter, or local may be named `Math`.
|
|
105
|
+
|
|
84
106
|
## Logical operators
|
|
85
107
|
|
|
86
108
|
Use `&&` and `||` with C# precedence: `&&` binds tighter than `||`, and
|