wuira 0.2.5 → 0.2.6
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 +13 -0
- package/README.md +1 -1
- package/docs/source-converters.md +9 -16
- package/lib/datasources/SourceConverter.d.ts +0 -9
- package/lib/datasources/SourceConverter.lua +0 -93
- package/lib/datasources/index.d.ts +2 -3
- package/lib/datasources/index.lua +0 -5
- package/package.json +1 -1
- package/lib/datasources/MathConverter.d.ts +0 -6
- package/lib/datasources/MathConverter.lua +0 -61
- package/lib/datasources/MathExpression.d.ts +0 -16
- package/lib/datasources/MathExpression.lua +0 -281
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ with the release date and highlight breaking changes explicitly.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## 0.2.6 — 2026-10-16
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Keep wuira source conversion callback-based. Case mappings and arithmetic
|
|
15
|
+
formula factories now belong to LumaUI's builder layer.
|
|
16
|
+
|
|
17
|
+
### Removed
|
|
18
|
+
|
|
19
|
+
- **Breaking:** Remove the `CaseConverter` and `MathConverter` APIs introduced in
|
|
20
|
+
0.2.5, including the expression parser. Use `new SourceConverter(source,
|
|
21
|
+
value => ...)` in wuira code; LumaUI owns its editable case/math definitions.
|
|
22
|
+
|
|
10
23
|
## 0.2.5 — 2026-10-16
|
|
11
24
|
|
|
12
25
|
### Added
|
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ wuira.Mount(App);
|
|
|
30
30
|
- **Native widgets** — Cells mount to WoW frames; progress bars use the native StatusBar widget.
|
|
31
31
|
- **Layout primitives** — Arrange children with `flow` and `grid`.
|
|
32
32
|
- **Data sources** — HP, Mana, auras, cooldowns, casts, swings, group members, class/spec, spellbook.
|
|
33
|
-
- **Source converters** —
|
|
33
|
+
- **Source converters** — Transform checked values with typed callbacks. See [source converters](docs/source-converters.md).
|
|
34
34
|
- **Slash commands** — Register commands with quoting and escaping support.
|
|
35
35
|
- **Windows and settings** — `Window`, `AddonSettings`, `FrameMover`, `KeybindCapture`.
|
|
36
36
|
- **Secure buttons** — Spell, item, macro, action, and target actions with `SecureButton`.
|
|
@@ -1,34 +1,27 @@
|
|
|
1
1
|
# Source converters
|
|
2
2
|
|
|
3
|
-
`SourceConverter` transforms a readable source
|
|
3
|
+
`SourceConverter` transforms a readable source with a typed callback. It consumes change notifications, shares one upstream subscription across observers, and does not create a timer or dispose the shared input. Sources without notifications remain available as checked one-time reads.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
|
-
import {
|
|
6
|
+
import { SourceConverter, HP, ClassSource, Unit } from "wuira/datasources";
|
|
7
7
|
|
|
8
|
+
const hp = new HP(Unit.Player);
|
|
9
|
+
const doubled = new SourceConverter(hp, value => value * 2);
|
|
8
10
|
const characterClass = new ClassSource(Unit.Player);
|
|
9
11
|
const token = new SourceConverter(characterClass, info => info?.token);
|
|
10
|
-
const color = new CaseConverter(token, [
|
|
11
|
-
{ equals: "PALADIN", value: { r: 1, g: 0.5, b: 0.7, a: 1 } },
|
|
12
|
-
{ equals: "MAGE", value: { r: 0.4, g: 0.8, b: 1, a: 1 } },
|
|
13
|
-
], { r: 0.35, g: 0.4, b: 0.45, a: 1 });
|
|
14
|
-
|
|
15
|
-
const hp = new HP(Unit.Player);
|
|
16
|
-
const doubled = new MathConverter(hp, "2 * input");
|
|
17
12
|
const stop = doubled.BindTo(value => console.log(value));
|
|
13
|
+
|
|
18
14
|
// On removal:
|
|
19
15
|
stop();
|
|
20
16
|
doubled.Dispose();
|
|
21
|
-
color.Dispose();
|
|
22
17
|
token.Dispose();
|
|
23
18
|
characterClass.Dispose();
|
|
24
19
|
```
|
|
25
20
|
|
|
26
|
-
`Value` and `Compute()` are checked reads. `TryCompute()` performs one checked read and returns `{ readable: false }` for restricted data
|
|
27
|
-
|
|
28
|
-
Cases compare strings, finite numbers and booleans with strict equality; `1` and `"1"` are different keys. Duplicate keys are rejected. Unmatched values return the supplied default, or `undefined` when none was provided. Case outputs can be colors, texture IDs, text or other data.
|
|
21
|
+
`Value` and `Compute()` are checked reads. `TryCompute()` performs one checked read and returns `{ readable: false }` for restricted data without invoking the callback. Converter chains use it to read their leaf once, including when the data is restricted.
|
|
29
22
|
|
|
30
|
-
|
|
23
|
+
`onChange()` observes future readable output changes; `BindTo()` also applies the current readable value immediately. Identical scalar outputs do not emit again. `When()` filters emissions without changing the checked getter. `Dispose()` releases observers while leaving the input available to other consumers.
|
|
31
24
|
|
|
32
|
-
Restricted values, including nested secret fields, never reach a conversion callback. Restrictions are checked when
|
|
25
|
+
Restricted values, including nested secret fields, never reach a conversion callback. Restrictions are checked when consumed; combat alone does not mark a value secret. ClassSource subscriptions follow unit identity changes and native events, releasing listeners when the last consumer unsubscribes.
|
|
33
26
|
|
|
34
|
-
|
|
27
|
+
The SDK accepts TypeScript callbacks. Editable case rules and math formulas are LumaUI builder factories, implemented using this generic source. Converters produce data; protected button actions remain subject to native combat restrictions.
|
|
@@ -28,12 +28,3 @@ export declare class SourceConverter<TInput, TOutput> extends DataSource<TOutput
|
|
|
28
28
|
BindTo(apply: (value: TOutput) => void): () => void;
|
|
29
29
|
Dispose(): void;
|
|
30
30
|
}
|
|
31
|
-
export type CaseKey = string | number | boolean;
|
|
32
|
-
export interface SourceCase<T> {
|
|
33
|
-
readonly equals: CaseKey;
|
|
34
|
-
readonly value: T;
|
|
35
|
-
}
|
|
36
|
-
/** Strict equality, first matching case, then the optional default value. */
|
|
37
|
-
export declare class CaseConverter<TInput, TOutput> extends SourceConverter<TInput, TOutput | undefined> {
|
|
38
|
-
constructor(input: ValueSource<TInput>, cases: readonly SourceCase<TOutput>[], fallback?: TOutput);
|
|
39
|
-
}
|
|
@@ -319,62 +319,6 @@ local function __TS__ClassExtends(target, base)
|
|
|
319
319
|
target.prototype.__tostring = base.prototype.__tostring
|
|
320
320
|
end
|
|
321
321
|
end
|
|
322
|
-
|
|
323
|
-
local function __TS__ObjectAssign(target, ...)
|
|
324
|
-
local sources = {...}
|
|
325
|
-
for i = 1, #sources do
|
|
326
|
-
local source = sources[i]
|
|
327
|
-
if type(source) == "table" then
|
|
328
|
-
for key in pairs(source) do
|
|
329
|
-
target[key] = source[key]
|
|
330
|
-
end
|
|
331
|
-
end
|
|
332
|
-
end
|
|
333
|
-
return target
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
local function __TS__ArrayMap(self, callbackfn, thisArg)
|
|
337
|
-
local result = {}
|
|
338
|
-
for i = 1, #self do
|
|
339
|
-
result[i] = callbackfn(thisArg, self[i], i - 1, self)
|
|
340
|
-
end
|
|
341
|
-
return result
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
local function __TS__TypeOf(value)
|
|
345
|
-
local luaType = type(value)
|
|
346
|
-
if luaType == "table" then
|
|
347
|
-
return "object"
|
|
348
|
-
elseif luaType == "nil" then
|
|
349
|
-
return "undefined"
|
|
350
|
-
else
|
|
351
|
-
return luaType
|
|
352
|
-
end
|
|
353
|
-
end
|
|
354
|
-
|
|
355
|
-
local function __TS__ArrayIncludes(self, searchElement, fromIndex)
|
|
356
|
-
if fromIndex == nil then
|
|
357
|
-
fromIndex = 0
|
|
358
|
-
end
|
|
359
|
-
local len = #self
|
|
360
|
-
local k = fromIndex
|
|
361
|
-
if fromIndex < 0 then
|
|
362
|
-
k = len + fromIndex
|
|
363
|
-
end
|
|
364
|
-
if k < 0 then
|
|
365
|
-
k = 0
|
|
366
|
-
end
|
|
367
|
-
for i = k + 1, len do
|
|
368
|
-
if self[i] == searchElement then
|
|
369
|
-
return true
|
|
370
|
-
end
|
|
371
|
-
end
|
|
372
|
-
return false
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
local function __TS__NumberIsFinite(value)
|
|
376
|
-
return type(value) == "number" and value == value and value ~= math.huge and value ~= -math.huge
|
|
377
|
-
end
|
|
378
322
|
-- End of Lua Library inline imports
|
|
379
323
|
local ____exports = {}
|
|
380
324
|
local ____DataSource = require("datasources.DataSource")
|
|
@@ -528,41 +472,4 @@ function SourceConverter.prototype.Dispose(self)
|
|
|
528
472
|
self.stop = nil
|
|
529
473
|
self.listeners:clear()
|
|
530
474
|
end
|
|
531
|
-
--- Strict equality, first matching case, then the optional default value.
|
|
532
|
-
____exports.CaseConverter = __TS__Class()
|
|
533
|
-
local CaseConverter = ____exports.CaseConverter
|
|
534
|
-
CaseConverter.name = "CaseConverter"
|
|
535
|
-
__TS__ClassExtends(CaseConverter, ____exports.SourceConverter)
|
|
536
|
-
function CaseConverter.prototype.____constructor(self, input, cases, fallback)
|
|
537
|
-
local entries = __TS__ArrayMap(
|
|
538
|
-
cases,
|
|
539
|
-
function(____, entry) return __TS__ObjectAssign({}, entry) end
|
|
540
|
-
)
|
|
541
|
-
local seen = __TS__New(Set)
|
|
542
|
-
for ____, entry in ipairs(entries) do
|
|
543
|
-
local key = entry.equals
|
|
544
|
-
if issecretvalue(key) or not __TS__ArrayIncludes(
|
|
545
|
-
{"string", "number", "boolean"},
|
|
546
|
-
__TS__TypeOf(key)
|
|
547
|
-
) or type(key) == "number" and not __TS__NumberIsFinite(key) then
|
|
548
|
-
error("CaseConverter: invalid match value")
|
|
549
|
-
end
|
|
550
|
-
if seen:has(key) then
|
|
551
|
-
error("CaseConverter: duplicate match value")
|
|
552
|
-
end
|
|
553
|
-
seen:add(key)
|
|
554
|
-
end
|
|
555
|
-
CaseConverter.____super.prototype.____constructor(
|
|
556
|
-
self,
|
|
557
|
-
input,
|
|
558
|
-
function(____, value)
|
|
559
|
-
for ____, entry in ipairs(entries) do
|
|
560
|
-
if value == entry.equals then
|
|
561
|
-
return entry.value
|
|
562
|
-
end
|
|
563
|
-
end
|
|
564
|
-
return fallback
|
|
565
|
-
end
|
|
566
|
-
)
|
|
567
|
-
end
|
|
568
475
|
return ____exports
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/// <reference path="../platform/forever.d.ts" />
|
|
2
2
|
export { DataSource } from "./DataSource";
|
|
3
3
|
export type { ReadResult } from "./DataSource";
|
|
4
|
-
export { SourceConverter
|
|
5
|
-
export type { ValueSource
|
|
6
|
-
export { MathConverter } from "./MathConverter";
|
|
4
|
+
export { SourceConverter } from "./SourceConverter";
|
|
5
|
+
export type { ValueSource } from "./SourceConverter";
|
|
7
6
|
export { EditModeLayouts } from "./EditModeLayouts";
|
|
8
7
|
export type { EditModeLayout, EditModeLayoutSnapshot, EditModeLayoutsStatus } from "./EditModeLayouts";
|
|
9
8
|
export { ProgressSource } from "./ProgressSource";
|
|
@@ -7,11 +7,6 @@ end
|
|
|
7
7
|
do
|
|
8
8
|
local ____SourceConverter = require("datasources.SourceConverter")
|
|
9
9
|
____exports.SourceConverter = ____SourceConverter.SourceConverter
|
|
10
|
-
____exports.CaseConverter = ____SourceConverter.CaseConverter
|
|
11
|
-
end
|
|
12
|
-
do
|
|
13
|
-
local ____MathConverter = require("datasources.MathConverter")
|
|
14
|
-
____exports.MathConverter = ____MathConverter.MathConverter
|
|
15
10
|
end
|
|
16
11
|
do
|
|
17
12
|
local ____EditModeLayouts = require("datasources.EditModeLayouts")
|
package/package.json
CHANGED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { SourceConverter } from "./SourceConverter";
|
|
2
|
-
import type { ValueSource } from "./SourceConverter";
|
|
3
|
-
export declare class MathConverter extends SourceConverter<number, number> {
|
|
4
|
-
constructor(input: ValueSource<number>, expression: string);
|
|
5
|
-
static Validate(expression: string): void;
|
|
6
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
|
2
|
-
-- Lua Library inline imports
|
|
3
|
-
local function __TS__Class(self)
|
|
4
|
-
local c = {prototype = {}}
|
|
5
|
-
c.prototype.__index = c.prototype
|
|
6
|
-
c.prototype.constructor = c
|
|
7
|
-
return c
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
local function __TS__ClassExtends(target, base)
|
|
11
|
-
target.____super = base
|
|
12
|
-
local staticMetatable = setmetatable({__index = base}, base)
|
|
13
|
-
setmetatable(target, staticMetatable)
|
|
14
|
-
local baseMetatable = getmetatable(base)
|
|
15
|
-
if baseMetatable then
|
|
16
|
-
if type(baseMetatable.__index) == "function" then
|
|
17
|
-
staticMetatable.__index = baseMetatable.__index
|
|
18
|
-
end
|
|
19
|
-
if type(baseMetatable.__newindex) == "function" then
|
|
20
|
-
staticMetatable.__newindex = baseMetatable.__newindex
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
setmetatable(target.prototype, base.prototype)
|
|
24
|
-
if type(base.prototype.__index) == "function" then
|
|
25
|
-
target.prototype.__index = base.prototype.__index
|
|
26
|
-
end
|
|
27
|
-
if type(base.prototype.__newindex) == "function" then
|
|
28
|
-
target.prototype.__newindex = base.prototype.__newindex
|
|
29
|
-
end
|
|
30
|
-
if type(base.prototype.__tostring) == "function" then
|
|
31
|
-
target.prototype.__tostring = base.prototype.__tostring
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
local function __TS__New(target, ...)
|
|
36
|
-
local instance = setmetatable({}, target.prototype)
|
|
37
|
-
instance:____constructor(...)
|
|
38
|
-
return instance
|
|
39
|
-
end
|
|
40
|
-
-- End of Lua Library inline imports
|
|
41
|
-
local ____exports = {}
|
|
42
|
-
local ____SourceConverter = require("datasources.SourceConverter")
|
|
43
|
-
local SourceConverter = ____SourceConverter.SourceConverter
|
|
44
|
-
local ____MathExpression = require("datasources.MathExpression")
|
|
45
|
-
local MathExpression = ____MathExpression.MathExpression
|
|
46
|
-
____exports.MathConverter = __TS__Class()
|
|
47
|
-
local MathConverter = ____exports.MathConverter
|
|
48
|
-
MathConverter.name = "MathConverter"
|
|
49
|
-
__TS__ClassExtends(MathConverter, SourceConverter)
|
|
50
|
-
function MathConverter.prototype.____constructor(self, input, expression)
|
|
51
|
-
local formula = __TS__New(MathExpression, expression)
|
|
52
|
-
SourceConverter.prototype.____constructor(
|
|
53
|
-
self,
|
|
54
|
-
input,
|
|
55
|
-
function(____, value) return formula:Evaluate(value) end
|
|
56
|
-
)
|
|
57
|
-
end
|
|
58
|
-
function MathConverter.Validate(self, expression)
|
|
59
|
-
__TS__New(MathExpression, expression)
|
|
60
|
-
end
|
|
61
|
-
return ____exports
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/** Small bounded arithmetic grammar. Expressions cannot call functions or execute Lua. */
|
|
2
|
-
export declare class MathExpression {
|
|
3
|
-
private readonly text;
|
|
4
|
-
private offset;
|
|
5
|
-
private token;
|
|
6
|
-
private tokens;
|
|
7
|
-
private readonly tree;
|
|
8
|
-
constructor(text: string);
|
|
9
|
-
private Next;
|
|
10
|
-
private Depth;
|
|
11
|
-
private Add;
|
|
12
|
-
private Multiply;
|
|
13
|
-
private Unary;
|
|
14
|
-
private Primary;
|
|
15
|
-
Evaluate(input: number): number;
|
|
16
|
-
}
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
|
2
|
-
-- Lua Library inline imports
|
|
3
|
-
local function __TS__Class(self)
|
|
4
|
-
local c = {prototype = {}}
|
|
5
|
-
c.prototype.__index = c.prototype
|
|
6
|
-
c.prototype.constructor = c
|
|
7
|
-
return c
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
local function __TS__StringAccess(self, index)
|
|
11
|
-
if index >= 0 and index < #self then
|
|
12
|
-
return string.sub(self, index + 1, index + 1)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
local function __TS__StringIncludes(self, searchString, position)
|
|
17
|
-
if not position then
|
|
18
|
-
position = 1
|
|
19
|
-
else
|
|
20
|
-
position = position + 1
|
|
21
|
-
end
|
|
22
|
-
local index = string.find(self, searchString, position, true)
|
|
23
|
-
return index ~= nil
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
local function __TS__StringSlice(self, start, ____end)
|
|
27
|
-
if start == nil or start ~= start then
|
|
28
|
-
start = 0
|
|
29
|
-
end
|
|
30
|
-
if ____end ~= ____end then
|
|
31
|
-
____end = 0
|
|
32
|
-
end
|
|
33
|
-
if start >= 0 then
|
|
34
|
-
start = start + 1
|
|
35
|
-
end
|
|
36
|
-
if ____end ~= nil and ____end < 0 then
|
|
37
|
-
____end = ____end - 1
|
|
38
|
-
end
|
|
39
|
-
return string.sub(self, start, ____end)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
local function __TS__Number(value)
|
|
43
|
-
local valueType = type(value)
|
|
44
|
-
if valueType == "number" then
|
|
45
|
-
return value
|
|
46
|
-
elseif valueType == "string" then
|
|
47
|
-
local numberValue = tonumber(value)
|
|
48
|
-
if numberValue then
|
|
49
|
-
return numberValue
|
|
50
|
-
end
|
|
51
|
-
if value == "Infinity" then
|
|
52
|
-
return math.huge
|
|
53
|
-
end
|
|
54
|
-
if value == "-Infinity" then
|
|
55
|
-
return -math.huge
|
|
56
|
-
end
|
|
57
|
-
local stringWithoutSpaces = string.gsub(value, "%s", "")
|
|
58
|
-
if stringWithoutSpaces == "" then
|
|
59
|
-
return 0
|
|
60
|
-
end
|
|
61
|
-
return 0 / 0
|
|
62
|
-
elseif valueType == "boolean" then
|
|
63
|
-
return value and 1 or 0
|
|
64
|
-
else
|
|
65
|
-
return 0 / 0
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
local function __TS__NumberIsFinite(value)
|
|
70
|
-
return type(value) == "number" and value == value and value ~= math.huge and value ~= -math.huge
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
local function __TS__ArrayIncludes(self, searchElement, fromIndex)
|
|
74
|
-
if fromIndex == nil then
|
|
75
|
-
fromIndex = 0
|
|
76
|
-
end
|
|
77
|
-
local len = #self
|
|
78
|
-
local k = fromIndex
|
|
79
|
-
if fromIndex < 0 then
|
|
80
|
-
k = len + fromIndex
|
|
81
|
-
end
|
|
82
|
-
if k < 0 then
|
|
83
|
-
k = 0
|
|
84
|
-
end
|
|
85
|
-
for i = k + 1, len do
|
|
86
|
-
if self[i] == searchElement then
|
|
87
|
-
return true
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
return false
|
|
91
|
-
end
|
|
92
|
-
-- End of Lua Library inline imports
|
|
93
|
-
local ____exports = {}
|
|
94
|
-
--- Small bounded arithmetic grammar. Expressions cannot call functions or execute Lua.
|
|
95
|
-
____exports.MathExpression = __TS__Class()
|
|
96
|
-
local MathExpression = ____exports.MathExpression
|
|
97
|
-
MathExpression.name = "MathExpression"
|
|
98
|
-
function MathExpression.prototype.____constructor(self, text)
|
|
99
|
-
self.text = text
|
|
100
|
-
self.offset = 0
|
|
101
|
-
self.token = ""
|
|
102
|
-
self.tokens = 0
|
|
103
|
-
if issecretvalue(text) or type(text) ~= "string" or #text == 0 or #text > 256 then
|
|
104
|
-
error("MathConverter: expression must contain 1–256 characters")
|
|
105
|
-
end
|
|
106
|
-
self:Next()
|
|
107
|
-
self.tree = self:Add(0)
|
|
108
|
-
if self.token ~= "" then
|
|
109
|
-
error("MathConverter: unexpected token " .. self.token)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
function MathExpression.prototype.Next(self)
|
|
113
|
-
local ____self_0, ____tokens_1 = self, "tokens"
|
|
114
|
-
local ____self_tokens_2 = ____self_0[____tokens_1] + 1
|
|
115
|
-
____self_0[____tokens_1] = ____self_tokens_2
|
|
116
|
-
if ____self_tokens_2 > 128 then
|
|
117
|
-
error("MathConverter: expression is too complex")
|
|
118
|
-
end
|
|
119
|
-
while __TS__StringIncludes(
|
|
120
|
-
" \t\r\n",
|
|
121
|
-
__TS__StringAccess(self.text, self.offset) or "\0"
|
|
122
|
-
) do
|
|
123
|
-
self.offset = self.offset + 1
|
|
124
|
-
end
|
|
125
|
-
local char = __TS__StringAccess(self.text, self.offset)
|
|
126
|
-
if char == nil then
|
|
127
|
-
self.token = ""
|
|
128
|
-
return
|
|
129
|
-
end
|
|
130
|
-
if __TS__StringSlice(self.text, self.offset, self.offset + 5) == "input" then
|
|
131
|
-
self.offset = self.offset + 5
|
|
132
|
-
self.token = "input"
|
|
133
|
-
return
|
|
134
|
-
end
|
|
135
|
-
if __TS__StringIncludes("0123456789.", char) then
|
|
136
|
-
local ____self_3, ____offset_4 = self, "offset"
|
|
137
|
-
local ____self_offset_5 = ____self_3[____offset_4]
|
|
138
|
-
____self_3[____offset_4] = ____self_offset_5 + 1
|
|
139
|
-
local start = ____self_offset_5
|
|
140
|
-
while __TS__StringIncludes(
|
|
141
|
-
"0123456789.",
|
|
142
|
-
__TS__StringAccess(self.text, self.offset) or "\0"
|
|
143
|
-
) do
|
|
144
|
-
self.offset = self.offset + 1
|
|
145
|
-
end
|
|
146
|
-
if __TS__StringAccess(self.text, self.offset) == "e" or __TS__StringAccess(self.text, self.offset) == "E" then
|
|
147
|
-
self.offset = self.offset + 1
|
|
148
|
-
if __TS__StringAccess(self.text, self.offset) == "+" or __TS__StringAccess(self.text, self.offset) == "-" then
|
|
149
|
-
self.offset = self.offset + 1
|
|
150
|
-
end
|
|
151
|
-
while __TS__StringIncludes(
|
|
152
|
-
"0123456789",
|
|
153
|
-
__TS__StringAccess(self.text, self.offset) or "\0"
|
|
154
|
-
) do
|
|
155
|
-
self.offset = self.offset + 1
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
self.token = __TS__StringSlice(self.text, start, self.offset)
|
|
159
|
-
if not __TS__NumberIsFinite(__TS__Number(self.token)) then
|
|
160
|
-
error("MathConverter: invalid number")
|
|
161
|
-
end
|
|
162
|
-
return
|
|
163
|
-
end
|
|
164
|
-
if __TS__StringIncludes("+-*/%^()", char) then
|
|
165
|
-
self.offset = self.offset + 1
|
|
166
|
-
self.token = char
|
|
167
|
-
if char == "*" and __TS__StringAccess(self.text, self.offset) == "*" then
|
|
168
|
-
self.offset = self.offset + 1
|
|
169
|
-
self.token = "^"
|
|
170
|
-
end
|
|
171
|
-
return
|
|
172
|
-
end
|
|
173
|
-
error("MathConverter: only input, numbers and arithmetic operators are allowed")
|
|
174
|
-
end
|
|
175
|
-
function MathExpression.prototype.Depth(self, depth)
|
|
176
|
-
if depth > 32 then
|
|
177
|
-
error("MathConverter: expression nesting is too deep")
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
function MathExpression.prototype.Add(self, depth)
|
|
181
|
-
local left = self:Multiply(depth)
|
|
182
|
-
while self.token == "+" or self.token == "-" do
|
|
183
|
-
local operator = self.token
|
|
184
|
-
self:Next()
|
|
185
|
-
left = {
|
|
186
|
-
kind = "binary",
|
|
187
|
-
operator = operator,
|
|
188
|
-
left = left,
|
|
189
|
-
right = self:Multiply(depth)
|
|
190
|
-
}
|
|
191
|
-
end
|
|
192
|
-
return left
|
|
193
|
-
end
|
|
194
|
-
function MathExpression.prototype.Multiply(self, depth)
|
|
195
|
-
local left = self:Unary(depth)
|
|
196
|
-
while __TS__ArrayIncludes({"*", "/", "%"}, self.token) do
|
|
197
|
-
local operator = self.token
|
|
198
|
-
self:Next()
|
|
199
|
-
left = {
|
|
200
|
-
kind = "binary",
|
|
201
|
-
operator = operator,
|
|
202
|
-
left = left,
|
|
203
|
-
right = self:Unary(depth)
|
|
204
|
-
}
|
|
205
|
-
end
|
|
206
|
-
return left
|
|
207
|
-
end
|
|
208
|
-
function MathExpression.prototype.Unary(self, depth)
|
|
209
|
-
self:Depth(depth)
|
|
210
|
-
if self.token == "+" or self.token == "-" then
|
|
211
|
-
local sign = self.token == "-" and -1 or 1
|
|
212
|
-
self:Next()
|
|
213
|
-
return {
|
|
214
|
-
kind = "unary",
|
|
215
|
-
sign = sign,
|
|
216
|
-
child = self:Unary(depth + 1)
|
|
217
|
-
}
|
|
218
|
-
end
|
|
219
|
-
local left = self:Primary(depth)
|
|
220
|
-
if self.token == "^" then
|
|
221
|
-
self:Next()
|
|
222
|
-
left = {
|
|
223
|
-
kind = "binary",
|
|
224
|
-
operator = "^",
|
|
225
|
-
left = left,
|
|
226
|
-
right = self:Unary(depth + 1)
|
|
227
|
-
}
|
|
228
|
-
end
|
|
229
|
-
return left
|
|
230
|
-
end
|
|
231
|
-
function MathExpression.prototype.Primary(self, depth)
|
|
232
|
-
self:Depth(depth)
|
|
233
|
-
if self.token == "input" then
|
|
234
|
-
self:Next()
|
|
235
|
-
return {kind = "input"}
|
|
236
|
-
end
|
|
237
|
-
if self.token == "(" then
|
|
238
|
-
self:Next()
|
|
239
|
-
local value = self:Add(depth + 1)
|
|
240
|
-
if self.token ~= ")" then
|
|
241
|
-
error("MathConverter: missing closing parenthesis")
|
|
242
|
-
end
|
|
243
|
-
self:Next()
|
|
244
|
-
return value
|
|
245
|
-
end
|
|
246
|
-
if self.token ~= "" and __TS__NumberIsFinite(__TS__Number(self.token)) then
|
|
247
|
-
local value = __TS__Number(self.token)
|
|
248
|
-
self:Next()
|
|
249
|
-
return {kind = "number", value = value}
|
|
250
|
-
end
|
|
251
|
-
error("MathConverter: expected input, a number or parentheses")
|
|
252
|
-
end
|
|
253
|
-
function MathExpression.prototype.Evaluate(self, input)
|
|
254
|
-
if issecretvalue(input) or type(input) ~= "number" or not __TS__NumberIsFinite(input) then
|
|
255
|
-
error("MathConverter: input must be a readable finite number")
|
|
256
|
-
end
|
|
257
|
-
local read
|
|
258
|
-
read = function(____, node)
|
|
259
|
-
if node.kind == "input" then
|
|
260
|
-
return input
|
|
261
|
-
end
|
|
262
|
-
if node.kind == "number" then
|
|
263
|
-
return node.value
|
|
264
|
-
end
|
|
265
|
-
if node.kind == "unary" then
|
|
266
|
-
return node.sign * read(nil, node.child)
|
|
267
|
-
end
|
|
268
|
-
local left = read(nil, node.left)
|
|
269
|
-
local right = read(nil, node.right)
|
|
270
|
-
if (node.operator == "/" or node.operator == "%") and right == 0 then
|
|
271
|
-
error("MathConverter: division by zero")
|
|
272
|
-
end
|
|
273
|
-
local result = node.operator == "+" and left + right or (node.operator == "-" and left - right or (node.operator == "*" and left * right or (node.operator == "/" and left / right or (node.operator == "%" and left % right or left ^ right))))
|
|
274
|
-
if not __TS__NumberIsFinite(result) then
|
|
275
|
-
error("MathConverter: result must be finite")
|
|
276
|
-
end
|
|
277
|
-
return result
|
|
278
|
-
end
|
|
279
|
-
return read(nil, self.tree)
|
|
280
|
-
end
|
|
281
|
-
return ____exports
|