@abaplint/runtime 2.13.93 → 2.13.94
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/build/src/builtin/_extremum.d.ts +9 -0
- package/build/src/builtin/_extremum.js +75 -0
- package/build/src/builtin/nmax.d.ts +1 -2
- package/build/src/builtin/nmax.js +3 -28
- package/build/src/builtin/nmin.d.ts +1 -2
- package/build/src/builtin/nmin.js +3 -28
- package/build/src/builtin/round.d.ts +14 -5
- package/build/src/builtin/round.js +69 -21
- package/build/src/compare/eq.js +10 -0
- package/build/src/compare/in.d.ts +3 -1
- package/build/src/compare/in.js +35 -18
- package/build/src/statements/create_data.js +14 -2
- package/build/src/statements/read_table.js +93 -7
- package/build/src/template_formatting.js +18 -4
- package/build/src/types/table.js +11 -1
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DecFloat34, Float, Integer, Integer8, Packed } from "../types";
|
|
2
|
+
import { INumeric } from "../types/_numeric";
|
|
3
|
+
export type ExtremumArgument = number | INumeric | undefined;
|
|
4
|
+
/** nmax( ) and nmin( ): the result has the calculation type of the arguments,
|
|
5
|
+
* as an arithmetic expression would determine it - decfloat34 before f before
|
|
6
|
+
* p before int8 before i. So nmin( val1 = i1 val2 = i2 ) is an i, which DO
|
|
7
|
+
* nmin( ... ) TIMES and every other integer position rely on, and a p result
|
|
8
|
+
* keeps the most decimals of its arguments. */
|
|
9
|
+
export declare function extremum(args: ExtremumArgument[], isBetter: (candidate: number, best: number) => boolean): Float | Integer8 | Integer | DecFloat34 | Packed;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extremum = extremum;
|
|
4
|
+
const _parse_1 = require("../operators/_parse");
|
|
5
|
+
const types_1 = require("../types");
|
|
6
|
+
// the calculation types in the order an arithmetic expression ranks them
|
|
7
|
+
const INT = 0;
|
|
8
|
+
const INT8 = 1;
|
|
9
|
+
const PACKED = 2;
|
|
10
|
+
const FLOAT = 3;
|
|
11
|
+
const DECFLOAT = 4;
|
|
12
|
+
/** nmax( ) and nmin( ): the result has the calculation type of the arguments,
|
|
13
|
+
* as an arithmetic expression would determine it - decfloat34 before f before
|
|
14
|
+
* p before int8 before i. So nmin( val1 = i1 val2 = i2 ) is an i, which DO
|
|
15
|
+
* nmin( ... ) TIMES and every other integer position rely on, and a p result
|
|
16
|
+
* keeps the most decimals of its arguments. */
|
|
17
|
+
function extremum(args, isBetter) {
|
|
18
|
+
let best = undefined;
|
|
19
|
+
let bestValue = 0;
|
|
20
|
+
let type = INT;
|
|
21
|
+
let decimals = 0;
|
|
22
|
+
for (let arg of args) {
|
|
23
|
+
if (arg === undefined) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (arg instanceof types_1.FieldSymbol) {
|
|
27
|
+
arg = arg.getPointer();
|
|
28
|
+
}
|
|
29
|
+
if (arg instanceof types_1.DecFloat34) {
|
|
30
|
+
type = Math.max(type, DECFLOAT);
|
|
31
|
+
}
|
|
32
|
+
else if (arg instanceof types_1.Float) {
|
|
33
|
+
type = Math.max(type, FLOAT);
|
|
34
|
+
}
|
|
35
|
+
else if (arg instanceof types_1.Packed) {
|
|
36
|
+
type = Math.max(type, PACKED);
|
|
37
|
+
decimals = Math.max(decimals, arg.getDecimals());
|
|
38
|
+
}
|
|
39
|
+
else if (arg instanceof types_1.Integer8) {
|
|
40
|
+
type = Math.max(type, INT8);
|
|
41
|
+
}
|
|
42
|
+
else if (arg instanceof types_1.Integer || (typeof arg === "number" && Number.isInteger(arg))) {
|
|
43
|
+
// i, the lowest type
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// character-like and other arguments, as before: f
|
|
47
|
+
type = Math.max(type, FLOAT);
|
|
48
|
+
}
|
|
49
|
+
const value = (0, _parse_1.parse)(arg);
|
|
50
|
+
if (best === undefined || isBetter(value, bestValue)) {
|
|
51
|
+
best = arg;
|
|
52
|
+
bestValue = value;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
switch (type) {
|
|
56
|
+
case DECFLOAT:
|
|
57
|
+
return new types_1.DecFloat34().set(bestValue);
|
|
58
|
+
case FLOAT:
|
|
59
|
+
return new types_1.Float().set(bestValue);
|
|
60
|
+
case PACKED:
|
|
61
|
+
if (best instanceof types_1.Packed) {
|
|
62
|
+
// through the digits, not through a binary float
|
|
63
|
+
return new types_1.Packed({ length: 16, decimals }).set(best.toFixed(best.getDecimals()));
|
|
64
|
+
}
|
|
65
|
+
return new types_1.Packed({ length: 16, decimals }).set(bestValue);
|
|
66
|
+
case INT8:
|
|
67
|
+
if (best instanceof types_1.Integer8) {
|
|
68
|
+
return new types_1.Integer8().set(best.get());
|
|
69
|
+
}
|
|
70
|
+
return new types_1.Integer8().set(bestValue);
|
|
71
|
+
default:
|
|
72
|
+
return new types_1.Integer().set(bestValue);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=_extremum.js.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { INumeric } from "../types/_numeric";
|
|
2
|
-
import { Float } from "../types";
|
|
3
2
|
export interface INmaxInput {
|
|
4
3
|
val1: number | INumeric;
|
|
5
4
|
val2: number | INumeric;
|
|
@@ -11,4 +10,4 @@ export interface INmaxInput {
|
|
|
11
10
|
val8?: number | INumeric;
|
|
12
11
|
val9?: number | INumeric;
|
|
13
12
|
}
|
|
14
|
-
export declare function nmax(input: INmaxInput): Float;
|
|
13
|
+
export declare function nmax(input: INmaxInput): import("../types").Float | import("../types").Integer8 | import("../types").Integer | import("../types").DecFloat34 | import("../types").Packed;
|
|
@@ -1,34 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.nmax = nmax;
|
|
4
|
-
const
|
|
5
|
-
const types_1 = require("../types");
|
|
4
|
+
const _extremum_1 = require("./_extremum");
|
|
6
5
|
function nmax(input) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
values.push((0, _parse_1.parse)(input.val2));
|
|
10
|
-
if (input.val3) {
|
|
11
|
-
values.push((0, _parse_1.parse)(input.val3));
|
|
12
|
-
}
|
|
13
|
-
if (input.val4) {
|
|
14
|
-
values.push((0, _parse_1.parse)(input.val4));
|
|
15
|
-
}
|
|
16
|
-
if (input.val5) {
|
|
17
|
-
values.push((0, _parse_1.parse)(input.val5));
|
|
18
|
-
}
|
|
19
|
-
if (input.val6) {
|
|
20
|
-
values.push((0, _parse_1.parse)(input.val6));
|
|
21
|
-
}
|
|
22
|
-
if (input.val7) {
|
|
23
|
-
values.push((0, _parse_1.parse)(input.val7));
|
|
24
|
-
}
|
|
25
|
-
if (input.val8) {
|
|
26
|
-
values.push((0, _parse_1.parse)(input.val8));
|
|
27
|
-
}
|
|
28
|
-
if (input.val9) {
|
|
29
|
-
values.push((0, _parse_1.parse)(input.val9));
|
|
30
|
-
}
|
|
31
|
-
values.sort((a, b) => (b - a));
|
|
32
|
-
return new types_1.Float().set(values[0]);
|
|
6
|
+
return (0, _extremum_1.extremum)([input.val1, input.val2, input.val3, input.val4, input.val5,
|
|
7
|
+
input.val6, input.val7, input.val8, input.val9], (candidate, best) => candidate > best);
|
|
33
8
|
}
|
|
34
9
|
//# sourceMappingURL=nmax.js.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { INumeric } from "../types/_numeric";
|
|
2
|
-
import { Float } from "../types";
|
|
3
2
|
export interface INminInput {
|
|
4
3
|
val1: number | INumeric;
|
|
5
4
|
val2: number | INumeric;
|
|
@@ -11,4 +10,4 @@ export interface INminInput {
|
|
|
11
10
|
val8?: number | INumeric;
|
|
12
11
|
val9?: number | INumeric;
|
|
13
12
|
}
|
|
14
|
-
export declare function nmin(input: INminInput): Float;
|
|
13
|
+
export declare function nmin(input: INminInput): import("../types").Float | import("../types").Integer8 | import("../types").Integer | import("../types").DecFloat34 | import("../types").Packed;
|
|
@@ -1,34 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.nmin = nmin;
|
|
4
|
-
const
|
|
5
|
-
const types_1 = require("../types");
|
|
4
|
+
const _extremum_1 = require("./_extremum");
|
|
6
5
|
function nmin(input) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
values.push((0, _parse_1.parse)(input.val2));
|
|
10
|
-
if (input.val3) {
|
|
11
|
-
values.push((0, _parse_1.parse)(input.val3));
|
|
12
|
-
}
|
|
13
|
-
if (input.val4) {
|
|
14
|
-
values.push((0, _parse_1.parse)(input.val4));
|
|
15
|
-
}
|
|
16
|
-
if (input.val5) {
|
|
17
|
-
values.push((0, _parse_1.parse)(input.val5));
|
|
18
|
-
}
|
|
19
|
-
if (input.val6) {
|
|
20
|
-
values.push((0, _parse_1.parse)(input.val6));
|
|
21
|
-
}
|
|
22
|
-
if (input.val7) {
|
|
23
|
-
values.push((0, _parse_1.parse)(input.val7));
|
|
24
|
-
}
|
|
25
|
-
if (input.val8) {
|
|
26
|
-
values.push((0, _parse_1.parse)(input.val8));
|
|
27
|
-
}
|
|
28
|
-
if (input.val9) {
|
|
29
|
-
values.push((0, _parse_1.parse)(input.val9));
|
|
30
|
-
}
|
|
31
|
-
values.sort((a, b) => (a - b));
|
|
32
|
-
return new types_1.Float().set(values[0]);
|
|
6
|
+
return (0, _extremum_1.extremum)([input.val1, input.val2, input.val3, input.val4, input.val5,
|
|
7
|
+
input.val6, input.val7, input.val8, input.val9], (candidate, best) => candidate < best);
|
|
33
8
|
}
|
|
34
9
|
//# sourceMappingURL=nmin.js.map
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DecFloat34 } from "../types";
|
|
2
2
|
import { ICharacter } from "../types/_character";
|
|
3
3
|
import { INumeric } from "../types/_numeric";
|
|
4
|
+
type Input = INumeric | ICharacter | string | number;
|
|
5
|
+
/** round( val = arg {dec = n}|{prec = n} [mode = m] )
|
|
6
|
+
*
|
|
7
|
+
* The argument is converted to decfloat34 and rounded as a decimal number, so
|
|
8
|
+
* round( val = '2.675' dec = 2 ) is 2.68 - not 2.67, which is what scaling the
|
|
9
|
+
* nearest binary float would give. A binary float argument brings its full
|
|
10
|
+
* binary expansion along, the way converting f to decfloat34 does. */
|
|
4
11
|
export declare function round(input: {
|
|
5
|
-
val:
|
|
6
|
-
dec
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
val: Input;
|
|
13
|
+
dec?: Input;
|
|
14
|
+
prec?: Input;
|
|
15
|
+
mode?: Input;
|
|
16
|
+
}): DecFloat34;
|
|
17
|
+
export {};
|
|
@@ -3,39 +3,87 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.round = round;
|
|
4
4
|
const _parse_1 = require("../operators/_parse");
|
|
5
5
|
const types_1 = require("../types");
|
|
6
|
+
// the rounding modes of CL_ABAP_MATH, the decNumber enumeration
|
|
7
|
+
const ROUND_CEILING = 0;
|
|
8
|
+
const ROUND_UP = 1;
|
|
9
|
+
const ROUND_HALF_UP = 2;
|
|
10
|
+
const ROUND_HALF_EVEN = 3;
|
|
11
|
+
const ROUND_HALF_DOWN = 4;
|
|
12
|
+
const ROUND_DOWN = 5;
|
|
13
|
+
const ROUND_FLOOR = 6;
|
|
14
|
+
/** round( val = arg {dec = n}|{prec = n} [mode = m] )
|
|
15
|
+
*
|
|
16
|
+
* The argument is converted to decfloat34 and rounded as a decimal number, so
|
|
17
|
+
* round( val = '2.675' dec = 2 ) is 2.68 - not 2.67, which is what scaling the
|
|
18
|
+
* nearest binary float would give. A binary float argument brings its full
|
|
19
|
+
* binary expansion along, the way converting f to decfloat34 does. */
|
|
6
20
|
function round(input) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
21
|
+
const mode = input.mode === undefined ? ROUND_HALF_UP : (0, _parse_1.parse)(input.mode);
|
|
22
|
+
let arg = input.val;
|
|
23
|
+
if (arg instanceof types_1.FieldSymbol) {
|
|
24
|
+
arg = arg.getPointer();
|
|
10
25
|
}
|
|
11
|
-
|
|
12
|
-
|
|
26
|
+
const val = (0, _parse_1.parse)(arg);
|
|
27
|
+
const text = arg instanceof types_1.Float ? val.toPrecision(34) : String(val);
|
|
28
|
+
const match = /^(-?)(\d*)\.?(\d*)(?:e([+-]?\d+))?$/i.exec(text);
|
|
29
|
+
if (match === null || !Number.isFinite(val)) {
|
|
30
|
+
return new types_1.DecFloat34().set(val);
|
|
13
31
|
}
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
32
|
+
const negative = match[1] === "-";
|
|
33
|
+
const digits = BigInt((match[2] + match[3]).replace(/^0+/, "") || "0");
|
|
34
|
+
// the value is digits * 10^exponent
|
|
35
|
+
const exponent = parseInt(match[4] || "0", 10) - match[3].length;
|
|
36
|
+
let dec;
|
|
37
|
+
if (input.prec !== undefined) {
|
|
38
|
+
const prec = (0, _parse_1.parse)(input.prec);
|
|
39
|
+
if (prec <= 0) {
|
|
40
|
+
throw new Error("CX_SY_ARITHMETIC_ERROR, round( ) prec must be positive");
|
|
41
|
+
}
|
|
42
|
+
// position of the most significant digit
|
|
43
|
+
const top = digits.toString().length - 1 + exponent;
|
|
44
|
+
dec = prec - 1 - top;
|
|
18
45
|
}
|
|
19
|
-
|
|
46
|
+
else {
|
|
47
|
+
dec = input.dec === undefined ? 0 : (0, _parse_1.parse)(input.dec);
|
|
48
|
+
}
|
|
49
|
+
const drop = -exponent - dec;
|
|
50
|
+
if (drop <= 0 || digits === 0n) {
|
|
51
|
+
return new types_1.DecFloat34().set(val);
|
|
52
|
+
}
|
|
53
|
+
const divisor = 10n ** BigInt(drop);
|
|
54
|
+
let kept = digits / divisor;
|
|
55
|
+
const rest = digits % divisor;
|
|
56
|
+
// compare the dropped part with one half of the last kept digit
|
|
57
|
+
const half = rest * 2n === divisor ? 0 : (rest * 2n > divisor ? 1 : -1);
|
|
58
|
+
let away;
|
|
20
59
|
switch (mode) {
|
|
21
|
-
case
|
|
22
|
-
|
|
60
|
+
case ROUND_CEILING:
|
|
61
|
+
away = rest > 0n && negative === false;
|
|
62
|
+
break;
|
|
63
|
+
case ROUND_UP:
|
|
64
|
+
away = rest > 0n;
|
|
23
65
|
break;
|
|
24
|
-
case
|
|
25
|
-
|
|
66
|
+
case ROUND_HALF_UP:
|
|
67
|
+
away = half >= 0;
|
|
26
68
|
break;
|
|
27
|
-
case
|
|
28
|
-
|
|
69
|
+
case ROUND_HALF_EVEN:
|
|
70
|
+
away = half > 0 || (half === 0 && kept % 2n === 1n);
|
|
29
71
|
break;
|
|
30
|
-
case
|
|
31
|
-
|
|
72
|
+
case ROUND_HALF_DOWN:
|
|
73
|
+
away = half > 0;
|
|
32
74
|
break;
|
|
33
|
-
case
|
|
34
|
-
|
|
75
|
+
case ROUND_DOWN:
|
|
76
|
+
away = false;
|
|
77
|
+
break;
|
|
78
|
+
case ROUND_FLOOR:
|
|
79
|
+
away = rest > 0n && negative === true;
|
|
35
80
|
break;
|
|
36
81
|
default:
|
|
37
82
|
throw new Error("round(), unknown mode: " + mode);
|
|
38
83
|
}
|
|
39
|
-
|
|
84
|
+
if (away) {
|
|
85
|
+
kept += 1n;
|
|
86
|
+
}
|
|
87
|
+
return new types_1.DecFloat34().set(Number((negative ? "-" : "") + kept.toString() + "e" + (-dec)));
|
|
40
88
|
}
|
|
41
89
|
//# sourceMappingURL=round.js.map
|
package/build/src/compare/eq.js
CHANGED
|
@@ -347,6 +347,16 @@ function eq(left, right) {
|
|
|
347
347
|
else if (right instanceof types_1.Float && typeof l === "number") {
|
|
348
348
|
r = right.getRaw();
|
|
349
349
|
}
|
|
350
|
+
else if ((right instanceof types_1.Float || right instanceof types_1.DecFloat34) && typeof l === "string") {
|
|
351
|
+
// a character-like operand is compared as a number: get( ) of a float
|
|
352
|
+
// writes 1,4800000000000000E+02, which no text compares equal with
|
|
353
|
+
r = right.getRaw();
|
|
354
|
+
l = Number(l);
|
|
355
|
+
}
|
|
356
|
+
else if (left instanceof types_1.DecFloat34 && typeof r === "string") {
|
|
357
|
+
l = left.getRaw();
|
|
358
|
+
r = Number(r);
|
|
359
|
+
}
|
|
350
360
|
else if (left instanceof types_1.Float) {
|
|
351
361
|
if (typeof r === "number") {
|
|
352
362
|
l = left.getRaw();
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Table } from "../types";
|
|
2
2
|
import { ICharacter } from "../types/_character";
|
|
3
3
|
import { INumeric } from "../types/_numeric";
|
|
4
|
-
|
|
4
|
+
type Operand = number | string | ICharacter | INumeric;
|
|
5
|
+
export declare function compareIn(left: Operand, right: Table): boolean;
|
|
6
|
+
export {};
|
package/build/src/compare/in.js
CHANGED
|
@@ -3,32 +3,49 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.compareIn = compareIn;
|
|
4
4
|
const cp_1 = require("./cp");
|
|
5
5
|
const eq_1 = require("./eq");
|
|
6
|
+
const ge_1 = require("./ge");
|
|
7
|
+
const gt_1 = require("./gt");
|
|
8
|
+
const le_1 = require("./le");
|
|
9
|
+
const lt_1 = require("./lt");
|
|
6
10
|
const ne_1 = require("./ne");
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
return
|
|
11
|
+
function matches(left, option, low, high) {
|
|
12
|
+
switch (option) {
|
|
13
|
+
case "EQ": return (0, eq_1.eq)(left, low);
|
|
14
|
+
case "NE": return (0, ne_1.ne)(left, low);
|
|
15
|
+
case "GT": return (0, gt_1.gt)(left, low);
|
|
16
|
+
case "GE": return (0, ge_1.ge)(left, low);
|
|
17
|
+
case "LT": return (0, lt_1.lt)(left, low);
|
|
18
|
+
case "LE": return (0, le_1.le)(left, low);
|
|
19
|
+
case "BT": return (0, ge_1.ge)(left, low) && (0, le_1.le)(left, high);
|
|
20
|
+
case "NB": return !((0, ge_1.ge)(left, low) && (0, le_1.le)(left, high));
|
|
21
|
+
case "CP": return (0, cp_1.cp)(left, low);
|
|
22
|
+
case "NP": return !(0, cp_1.cp)(left, low);
|
|
23
|
+
default: throw new Error("compareIn, unknown option " + option);
|
|
10
24
|
}
|
|
25
|
+
}
|
|
26
|
+
// A value is in a range table when some I row admits it, or there is no I
|
|
27
|
+
// row at all, and no E row admits it. An empty table admits everything.
|
|
28
|
+
function compareIn(left, right) {
|
|
29
|
+
let included = false;
|
|
30
|
+
let hasInclude = false;
|
|
11
31
|
for (const row of right.array()) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
32
|
+
const r = row.get();
|
|
33
|
+
const sign = r["sign"].get().toString().toUpperCase().trim();
|
|
34
|
+
const option = r["option"].get().toString().toUpperCase().trim();
|
|
35
|
+
const hit = matches(left, option, r["low"], r["high"]);
|
|
36
|
+
if (sign === "I") {
|
|
37
|
+
hasInclude = true;
|
|
38
|
+
included = included || hit;
|
|
21
39
|
}
|
|
22
|
-
else if (
|
|
23
|
-
if (
|
|
24
|
-
return
|
|
40
|
+
else if (sign === "E") {
|
|
41
|
+
if (hit) {
|
|
42
|
+
return false;
|
|
25
43
|
}
|
|
26
44
|
}
|
|
27
45
|
else {
|
|
28
|
-
|
|
29
|
-
throw new Error("compareIn todo");
|
|
46
|
+
throw new Error("compareIn, unknown sign " + sign);
|
|
30
47
|
}
|
|
31
48
|
}
|
|
32
|
-
return false;
|
|
49
|
+
return hasInclude === false || included;
|
|
33
50
|
}
|
|
34
51
|
//# sourceMappingURL=in.js.map
|
|
@@ -21,6 +21,18 @@ function createDataRefTo(target, name) {
|
|
|
21
21
|
}
|
|
22
22
|
target.assign(new abap.types.ABAPObject({ qualifiedName: name, RTTIName: name }));
|
|
23
23
|
}
|
|
24
|
+
/** CREATE DATA ... LIKE dobj takes the type of dobj, never its content: the
|
|
25
|
+
* created data object starts initial. A table is created empty right away
|
|
26
|
+
* instead of copying its rows first. */
|
|
27
|
+
function initialLike(like) {
|
|
28
|
+
if (like instanceof types_1.Table || like instanceof types_1.HashedTable) {
|
|
29
|
+
const TableType = like.constructor;
|
|
30
|
+
return new TableType(like.getRowType(), like.getOptions(), like.getQualifiedName());
|
|
31
|
+
}
|
|
32
|
+
const copy = like.clone();
|
|
33
|
+
copy.clear();
|
|
34
|
+
return copy;
|
|
35
|
+
}
|
|
24
36
|
function createData(target, options) {
|
|
25
37
|
// console.dir(options);
|
|
26
38
|
if (target instanceof types_1.FieldSymbol) {
|
|
@@ -208,10 +220,10 @@ function createData(target, options) {
|
|
|
208
220
|
options.like = options.like.getPointer();
|
|
209
221
|
}
|
|
210
222
|
if (options.table === true) {
|
|
211
|
-
target.assign(new abap.types.Table(options.like
|
|
223
|
+
target.assign(new abap.types.Table(initialLike(options.like), tableOptions));
|
|
212
224
|
}
|
|
213
225
|
else {
|
|
214
|
-
target.assign(options.like
|
|
226
|
+
target.assign(initialLike(options.like));
|
|
215
227
|
}
|
|
216
228
|
}
|
|
217
229
|
else {
|
|
@@ -90,6 +90,73 @@ async function searchWithKeyPromise(arr, withKey, startIndex = 0, usesTableLine)
|
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
/////////////////
|
|
93
|
+
function toRow(a, isStructured, usesTableLine) {
|
|
94
|
+
if (usesTableLine === false && isStructured === true) {
|
|
95
|
+
return a.get();
|
|
96
|
+
}
|
|
97
|
+
return isStructured ? { table_line: a, ...a.get() } : { table_line: a };
|
|
98
|
+
}
|
|
99
|
+
/** READ with a key on a SORTED table: binary search over the leading key components that are given,
|
|
100
|
+
* a miss gives sy-subrc 4 and sy-tabix of the row the key would be inserted before,
|
|
101
|
+
* or sy-subrc 8 and sy-tabix lines + 1 when it would be inserted after the last row */
|
|
102
|
+
/** true when every component of the condition is a component of the key */
|
|
103
|
+
function onlyKeyComponents(keyFields, options) {
|
|
104
|
+
const key = keyFields.map(k => k.toLowerCase());
|
|
105
|
+
return Object.keys(options.withKeySimple || {}).every(n => key.includes(n.toLowerCase()));
|
|
106
|
+
}
|
|
107
|
+
function readSortedWithKey(arr, keyFields, options) {
|
|
108
|
+
const names = Object.keys(options.withKeySimple).map(n => n.toLowerCase());
|
|
109
|
+
const prefix = [];
|
|
110
|
+
for (const keyField of keyFields) {
|
|
111
|
+
const index = names.indexOf(keyField.toLowerCase());
|
|
112
|
+
if (index < 0) {
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
prefix.push(options.withKeyValue[index]);
|
|
116
|
+
}
|
|
117
|
+
if (prefix.length === 0) {
|
|
118
|
+
const searchResult = searchWithKey(arr, options.withKey, 0, options.usesTableLine);
|
|
119
|
+
return { found: searchResult.found, foundIndex: searchResult.foundIndex, subrc: undefined };
|
|
120
|
+
}
|
|
121
|
+
const isStructured = arr[0] instanceof types_1.Structure;
|
|
122
|
+
const comparePrefix = (row) => {
|
|
123
|
+
for (const p of prefix) {
|
|
124
|
+
const value = p.key(row);
|
|
125
|
+
// eq first, lt and gt compare a c operand with its trailing blanks against a string
|
|
126
|
+
if ((0, compare_1.eq)(value, p.value)) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
else if ((0, compare_1.lt)(value, p.value)) {
|
|
130
|
+
return -1;
|
|
131
|
+
}
|
|
132
|
+
else if ((0, compare_1.gt)(value, p.value)) {
|
|
133
|
+
return 1;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return 0;
|
|
137
|
+
};
|
|
138
|
+
let left = 0;
|
|
139
|
+
let right = arr.length;
|
|
140
|
+
while (left < right) {
|
|
141
|
+
const middle = Math.floor((left + right) / 2);
|
|
142
|
+
if (comparePrefix(toRow(arr[middle], isStructured, options.usesTableLine)) < 0) {
|
|
143
|
+
left = middle + 1;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
right = middle;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (let index = left; index < arr.length; index++) {
|
|
150
|
+
const row = toRow(arr[index], isStructured, options.usesTableLine);
|
|
151
|
+
if (comparePrefix(row) !== 0) {
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
if (options.withKey(row) === true) {
|
|
155
|
+
return { found: arr[index], foundIndex: index + 1, subrc: undefined };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { found: undefined, foundIndex: left + 1, subrc: left >= arr.length ? 8 : 4 };
|
|
159
|
+
}
|
|
93
160
|
function readTable(table, options) {
|
|
94
161
|
let found = undefined;
|
|
95
162
|
let foundIndex = 0;
|
|
@@ -187,15 +254,34 @@ function readTable(table, options) {
|
|
|
187
254
|
// fallback
|
|
188
255
|
return readTable(table, { ...options, withKeySimple: undefined });
|
|
189
256
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
257
|
+
if (keyInformation?.type === types_1.TableAccessType.sorted
|
|
258
|
+
&& options.withKeyValue
|
|
259
|
+
&& onlyKeyComponents(keyInformation.keyFields, options)) {
|
|
260
|
+
const result = readSortedWithKey(arr, keyInformation.keyFields, options);
|
|
261
|
+
found = result.found;
|
|
262
|
+
foundIndex = result.foundIndex;
|
|
263
|
+
binarySubrc = result.subrc;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
const startIndex = (0, binary_search_1.binarySearchFrom)(arr, 0, arr.length, firstKeyName.toLowerCase(), firstValue) - 1;
|
|
267
|
+
if (startIndex >= 0) {
|
|
268
|
+
const searchResult = searchWithKeyEarlyExit(arr, options.withKey, startIndex, options.usesTableLine, firstKeyName, firstValue);
|
|
269
|
+
found = searchResult.found;
|
|
270
|
+
foundIndex = searchResult.foundIndex;
|
|
271
|
+
}
|
|
197
272
|
}
|
|
198
273
|
}
|
|
274
|
+
else if (options?.withTableKey === true
|
|
275
|
+
&& options.withKeyValue
|
|
276
|
+
&& options.withKeySimple
|
|
277
|
+
&& options.withKey
|
|
278
|
+
&& table.getOptions().primaryKey?.type === types_1.TableAccessType.sorted
|
|
279
|
+
&& onlyKeyComponents(table.getOptions().primaryKey?.keyFields || [], options)) {
|
|
280
|
+
const result = readSortedWithKey(table.array(), table.getOptions().primaryKey?.keyFields || [], options);
|
|
281
|
+
found = result.found;
|
|
282
|
+
foundIndex = result.foundIndex;
|
|
283
|
+
binarySubrc = result.subrc;
|
|
284
|
+
}
|
|
199
285
|
else if ((options?.binarySearch === true || options?.withTableKey === true)
|
|
200
286
|
&& options.withKeyValue
|
|
201
287
|
&& (options?.binarySearch === true || table.getOptions().primaryKey?.type !== types_1.TableAccessType.standard)
|
|
@@ -3,6 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.templateFormatting = templateFormatting;
|
|
4
4
|
const alpha_1 = require("./alpha");
|
|
5
5
|
const types_1 = require("./types");
|
|
6
|
+
/** DECIMALS for a decimal number: its decimal digits are rounded, half away
|
|
7
|
+
* from zero, the way ABAP rounds commercially - 2.675 gives 2.68, where
|
|
8
|
+
* toFixed( ) on the nearest binary float gives 2.67 */
|
|
9
|
+
function decimalsOfDecimal(value, decimals) {
|
|
10
|
+
const shift = (x, places) => {
|
|
11
|
+
const [mantissa, exponent] = (x + "").split("e");
|
|
12
|
+
return Number(mantissa + "e" + (Number(exponent || 0) + places));
|
|
13
|
+
};
|
|
14
|
+
const rounded = shift(Math.round(shift(Math.abs(value), decimals)), -decimals);
|
|
15
|
+
return (value < 0 && rounded !== 0 ? "-" : "") + rounded.toFixed(decimals);
|
|
16
|
+
}
|
|
6
17
|
function templateFormatting(source, options) {
|
|
7
18
|
let text = "";
|
|
8
19
|
if (source instanceof types_1.FieldSymbol) {
|
|
@@ -22,7 +33,10 @@ function templateFormatting(source, options) {
|
|
|
22
33
|
}
|
|
23
34
|
else if (source instanceof types_1.DecFloat34) {
|
|
24
35
|
const raw = source.getRaw();
|
|
25
|
-
if (
|
|
36
|
+
if (options?.decimals !== undefined) {
|
|
37
|
+
text = decimalsOfDecimal(raw, options.decimals);
|
|
38
|
+
}
|
|
39
|
+
else if (Number.isInteger(raw)) {
|
|
26
40
|
text = raw.toFixed(0);
|
|
27
41
|
}
|
|
28
42
|
else {
|
|
@@ -43,7 +57,7 @@ function templateFormatting(source, options) {
|
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
59
|
else if (source instanceof types_1.Packed) {
|
|
46
|
-
if (options?.decimals) {
|
|
60
|
+
if (options?.decimals !== undefined) {
|
|
47
61
|
text = source.toFixed(options.decimals);
|
|
48
62
|
}
|
|
49
63
|
else {
|
|
@@ -127,10 +141,10 @@ function templateFormatting(source, options) {
|
|
|
127
141
|
else if (options.width) {
|
|
128
142
|
text = text.trimEnd().padEnd(options.width, " ");
|
|
129
143
|
}
|
|
130
|
-
else if (options.decimals && source instanceof types_1.Integer) {
|
|
144
|
+
else if (options.decimals !== undefined && source instanceof types_1.Integer) {
|
|
131
145
|
text = source.get().toFixed(options.decimals);
|
|
132
146
|
}
|
|
133
|
-
else if (options.decimals && source instanceof types_1.Float) {
|
|
147
|
+
else if (options.decimals !== undefined && source instanceof types_1.Float) {
|
|
134
148
|
text = source.getRaw().toFixed(options.decimals);
|
|
135
149
|
}
|
|
136
150
|
}
|
package/build/src/types/table.js
CHANGED
|
@@ -550,8 +550,18 @@ class Table {
|
|
|
550
550
|
return val;
|
|
551
551
|
}
|
|
552
552
|
}
|
|
553
|
-
/* appends and returns this
|
|
553
|
+
/* appends and returns this - how VALUE #( ) builds a table, row by row.
|
|
554
|
+
* A sorted table gets each row at its sorted place, as INSERT ... INTO
|
|
555
|
+
* TABLE puts it: pushed, it was in insertion order until something
|
|
556
|
+
* assigned it, so a VALUE #( ) handed straight to a method or looped over
|
|
557
|
+
* was a SORTED table unsorted, and every binary search on it could miss.
|
|
558
|
+
* A duplicate of a unique key is kept, as before */
|
|
554
559
|
appendThis(item) {
|
|
560
|
+
const primary = this.getOptions()?.primaryKey;
|
|
561
|
+
if (primary?.type === TableAccessType.sorted && !(item instanceof data_reference_1.DataReference)) {
|
|
562
|
+
this.insertSorted(item, (a, b) => (0, sort_1.compareRows)(a, b, primary.keyFields), false);
|
|
563
|
+
return this;
|
|
564
|
+
}
|
|
555
565
|
this.append(item);
|
|
556
566
|
return this;
|
|
557
567
|
}
|