@abaplint/runtime 2.13.86 → 2.13.88

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.
Files changed (38) hide show
  1. package/build/src/builtin/_position.d.ts +12 -0
  2. package/build/src/builtin/_position.js +20 -0
  3. package/build/src/builtin/contains_any_not_of.d.ts +10 -0
  4. package/build/src/builtin/contains_any_not_of.js +28 -0
  5. package/build/src/builtin/contains_any_of.d.ts +10 -0
  6. package/build/src/builtin/contains_any_of.js +28 -0
  7. package/build/src/builtin/count.js +5 -4
  8. package/build/src/builtin/escape.js +2 -1
  9. package/build/src/builtin/find.js +3 -2
  10. package/build/src/builtin/find_any_not_of.js +4 -3
  11. package/build/src/builtin/find_any_of.js +4 -3
  12. package/build/src/builtin/index.d.ts +2 -0
  13. package/build/src/builtin/index.js +2 -0
  14. package/build/src/builtin/insert.js +4 -3
  15. package/build/src/builtin/repeat.js +2 -1
  16. package/build/src/builtin/replace.js +6 -7
  17. package/build/src/builtin/segment.js +2 -4
  18. package/build/src/builtin/shift_left.js +3 -5
  19. package/build/src/builtin/substring.js +3 -2
  20. package/build/src/compare/gt.js +5 -12
  21. package/build/src/operators/add.js +8 -0
  22. package/build/src/operators/minus.js +8 -0
  23. package/build/src/operators/mod.d.ts +2 -2
  24. package/build/src/operators/mod.js +5 -6
  25. package/build/src/operators/multiply.js +8 -0
  26. package/build/src/statements/create_data.js +23 -4
  27. package/build/src/statements/create_object.d.ts +17 -0
  28. package/build/src/statements/create_object.js +45 -0
  29. package/build/src/statements/index.d.ts +2 -0
  30. package/build/src/statements/index.js +2 -0
  31. package/build/src/statements/loop.js +20 -1
  32. package/build/src/types/decfloat34.js +4 -1
  33. package/build/src/types/float.js +7 -2
  34. package/build/src/types/integer.d.ts +1 -0
  35. package/build/src/types/integer.js +11 -3
  36. package/build/src/types/integer8.js +1 -1
  37. package/build/src/types/table.js +11 -2
  38. package/package.json +2 -2
@@ -0,0 +1,12 @@
1
+ import { ICharacter } from "../types/_character";
2
+ import { INumeric } from "../types/_numeric";
3
+ /** Reads an offset, length or occurrence argument as a number.
4
+ *
5
+ * parse() rather than get(): INumeric.get() promises a number, but Float and DecFloat34
6
+ * hand out their ABAP display form instead, ie. "1,2000000000000000E+01". Reading that
7
+ * back as a position keeps only the leading digit of the mantissa, so a length of 12
8
+ * silently becomes 1. nmin( ) and nmax( ) return a Float, and the syntax check lets them
9
+ * through where it rejects a declared TYPE f, so they are the way a float gets here.
10
+ *
11
+ * Rounded because ABAP converts the argument to type i, the same as assigning to one. */
12
+ export declare function position(input: INumeric | ICharacter | string | number | undefined): number | undefined;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.position = position;
4
+ const _parse_1 = require("../operators/_parse");
5
+ /** Reads an offset, length or occurrence argument as a number.
6
+ *
7
+ * parse() rather than get(): INumeric.get() promises a number, but Float and DecFloat34
8
+ * hand out their ABAP display form instead, ie. "1,2000000000000000E+01". Reading that
9
+ * back as a position keeps only the leading digit of the mantissa, so a length of 12
10
+ * silently becomes 1. nmin( ) and nmax( ) return a Float, and the syntax check lets them
11
+ * through where it rejects a declared TYPE f, so they are the way a float gets here.
12
+ *
13
+ * Rounded because ABAP converts the argument to type i, the same as assigning to one. */
14
+ function position(input) {
15
+ if (input === undefined) {
16
+ return undefined;
17
+ }
18
+ return Math.round((0, _parse_1.parse)(input));
19
+ }
20
+ //# sourceMappingURL=_position.js.map
@@ -0,0 +1,10 @@
1
+ import { String } from "../types";
2
+ import { ICharacter } from "../types/_character";
3
+ import { INumeric } from "../types/_numeric";
4
+ export interface IContainsAnyNotOfInput {
5
+ val: ICharacter | string;
6
+ sub: ICharacter | string;
7
+ off?: INumeric | number;
8
+ len?: INumeric | number;
9
+ }
10
+ export declare function contains_any_not_of(input: IContainsAnyNotOfInput): String;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.contains_any_not_of = contains_any_not_of;
4
+ const types_1 = require("../types");
5
+ const throw_error_1 = require("../throw_error");
6
+ function contains_any_not_of(input) {
7
+ const val = typeof input.val === "string" ? input.val : input.val.get();
8
+ const sub = typeof input.sub === "string" ? input.sub : input.sub.get();
9
+ const off = typeof input.off === "number" ? input.off : input.off?.get() || 0;
10
+ const len = typeof input.len === "number" ? input.len : input.len?.get();
11
+ if (off < 0 || off > val.length) {
12
+ (0, throw_error_1.throwError)("CX_SY_RANGE_OUT_OF_BOUNDS");
13
+ }
14
+ if (len !== undefined && (len < 0 || off + len > val.length)) {
15
+ (0, throw_error_1.throwError)("CX_SY_RANGE_OUT_OF_BOUNDS");
16
+ }
17
+ const end = len === undefined ? val.length : off + len;
18
+ const characters = new Set(sub.split(""));
19
+ let found = false;
20
+ for (let i = off; i < end; i++) {
21
+ if (!characters.has(val.charAt(i))) {
22
+ found = true;
23
+ break;
24
+ }
25
+ }
26
+ return new types_1.String().set(found ? "X" : " ");
27
+ }
28
+ //# sourceMappingURL=contains_any_not_of.js.map
@@ -0,0 +1,10 @@
1
+ import { String } from "../types";
2
+ import { ICharacter } from "../types/_character";
3
+ import { INumeric } from "../types/_numeric";
4
+ export interface IContainsAnyOfInput {
5
+ val: ICharacter | string;
6
+ sub: ICharacter | string;
7
+ off?: INumeric | number;
8
+ len?: INumeric | number;
9
+ }
10
+ export declare function contains_any_of(input: IContainsAnyOfInput): String;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.contains_any_of = contains_any_of;
4
+ const types_1 = require("../types");
5
+ const throw_error_1 = require("../throw_error");
6
+ function contains_any_of(input) {
7
+ const val = typeof input.val === "string" ? input.val : input.val.get();
8
+ const sub = typeof input.sub === "string" ? input.sub : input.sub.get();
9
+ const off = typeof input.off === "number" ? input.off : input.off?.get() || 0;
10
+ const len = typeof input.len === "number" ? input.len : input.len?.get();
11
+ if (off < 0 || off > val.length) {
12
+ (0, throw_error_1.throwError)("CX_SY_RANGE_OUT_OF_BOUNDS");
13
+ }
14
+ if (len !== undefined && (len < 0 || off + len > val.length)) {
15
+ (0, throw_error_1.throwError)("CX_SY_RANGE_OUT_OF_BOUNDS");
16
+ }
17
+ const end = len === undefined ? val.length : off + len;
18
+ const characters = new Set(sub.split(""));
19
+ let found = false;
20
+ for (let i = off; i < end; i++) {
21
+ if (characters.has(val.charAt(i))) {
22
+ found = true;
23
+ break;
24
+ }
25
+ }
26
+ return new types_1.String().set(found ? "X" : " ");
27
+ }
28
+ //# sourceMappingURL=contains_any_of.js.map
@@ -4,15 +4,16 @@ exports.count = count;
4
4
  const abap_regex_1 = require("../abap_regex");
5
5
  const compare_1 = require("../compare");
6
6
  const types_1 = require("../types");
7
+ const _position_1 = require("./_position");
7
8
  function count(input) {
8
9
  let found = 0;
9
10
  let val = input.val.get();
10
- if (input.off) {
11
- const off = input.off.get();
11
+ const off = (0, _position_1.position)(input.off);
12
+ if (off !== undefined) {
12
13
  val = val.substring(off);
13
14
  }
14
- if (input.len) {
15
- const len = input.len.get();
15
+ const len = (0, _position_1.position)(input.len);
16
+ if (len !== undefined) {
16
17
  val = val.substring(0, len);
17
18
  }
18
19
  let reg = "";
@@ -2,9 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.escape = escape;
4
4
  const types_1 = require("../types");
5
+ const _position_1 = require("./_position");
5
6
  function escape(input) {
6
7
  let val = typeof input.val === "string" ? input.val : input.val.get();
7
- const format = typeof input.format === "number" ? input.format : input.format.get();
8
+ const format = (0, _position_1.position)(input.format);
8
9
  // todo, optimize/cache regexes
9
10
  switch (format) {
10
11
  case 1: // e_xml_attr
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.find = find;
4
4
  const throw_error_1 = require("../throw_error");
5
5
  const types_1 = require("../types");
6
+ const _position_1 = require("./_position");
6
7
  function find(input) {
7
8
  let val = typeof input.val === "string" ? input.val : input.val.get();
8
9
  if (input.len !== undefined) {
@@ -32,8 +33,8 @@ function find(input) {
32
33
  }
33
34
  else {
34
35
  const sub = typeof input.sub === "string" ? input.sub : input.sub?.get();
35
- let off = typeof input.off === "number" ? input.off : input.off?.get() || 0;
36
- let occ = typeof input.occ === "number" ? input.occ : input.occ?.get();
36
+ let off = (0, _position_1.position)(input.off) || 0;
37
+ let occ = (0, _position_1.position)(input.occ);
37
38
  if (occ === 0) {
38
39
  (0, throw_error_1.throwError)("CX_SY_STRG_PAR_VAL");
39
40
  }
@@ -3,12 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.find_any_not_of = find_any_not_of;
4
4
  const types_1 = require("../types");
5
5
  const throw_error_1 = require("../throw_error");
6
+ const _position_1 = require("./_position");
6
7
  function find_any_not_of(input) {
7
8
  const val = typeof input.val === "string" ? input.val : input.val.get();
8
9
  const sub = typeof input.sub === "string" ? input.sub : input.sub.get();
9
- const off = typeof input.off === "number" ? input.off : input.off?.get() || 0;
10
- const len = typeof input.len === "number" ? input.len : input.len?.get();
11
- let occ = typeof input.occ === "number" ? input.occ : input.occ?.get();
10
+ const off = (0, _position_1.position)(input.off) || 0;
11
+ const len = (0, _position_1.position)(input.len);
12
+ let occ = (0, _position_1.position)(input.occ);
12
13
  if (occ === 0) {
13
14
  (0, throw_error_1.throwError)("CX_SY_STRG_PAR_VAL");
14
15
  }
@@ -3,12 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.find_any_of = find_any_of;
4
4
  const types_1 = require("../types");
5
5
  const throw_error_1 = require("../throw_error");
6
+ const _position_1 = require("./_position");
6
7
  function find_any_of(input) {
7
8
  const val = typeof input.val === "string" ? input.val : input.val.get();
8
9
  const sub = typeof input.sub === "string" ? input.sub : input.sub.get();
9
- const off = typeof input.off === "number" ? input.off : input.off?.get() || 0;
10
- const len = typeof input.len === "number" ? input.len : input.len?.get();
11
- let occ = typeof input.occ === "number" ? input.occ : input.occ?.get();
10
+ const off = (0, _position_1.position)(input.off) || 0;
11
+ const len = (0, _position_1.position)(input.len);
12
+ let occ = (0, _position_1.position)(input.occ);
12
13
  if (occ === 0) {
13
14
  (0, throw_error_1.throwError)("CX_SY_STRG_PAR_VAL");
14
15
  }
@@ -8,6 +8,8 @@ export * from "./ceil";
8
8
  export * from "./concat_lines_of";
9
9
  export * from "./condense";
10
10
  export * from "./contains";
11
+ export * from "./contains_any_not_of";
12
+ export * from "./contains_any_of";
11
13
  export * from "./cos";
12
14
  export * from "./cosh";
13
15
  export * from "./count_any_of";
@@ -25,6 +25,8 @@ __exportStar(require("./ceil"), exports);
25
25
  __exportStar(require("./concat_lines_of"), exports);
26
26
  __exportStar(require("./condense"), exports);
27
27
  __exportStar(require("./contains"), exports);
28
+ __exportStar(require("./contains_any_not_of"), exports);
29
+ __exportStar(require("./contains_any_of"), exports);
28
30
  __exportStar(require("./cos"), exports);
29
31
  __exportStar(require("./cosh"), exports);
30
32
  __exportStar(require("./count_any_of"), exports);
@@ -2,10 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.insert = insert;
4
4
  const types_1 = require("../types");
5
+ const _position_1 = require("./_position");
5
6
  function insert(input) {
6
- let offset = 0;
7
- if (input.off) {
8
- offset = input.off.get();
7
+ let offset = (0, _position_1.position)(input.off);
8
+ if (offset === undefined) {
9
+ offset = 0;
9
10
  }
10
11
  const value = input.val.getOffset({ offset: 0, length: offset }).get() +
11
12
  input.sub.get() +
@@ -2,8 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.repeat = repeat;
4
4
  const string_1 = require("../types/string");
5
+ const _position_1 = require("./_position");
5
6
  function repeat(input) {
6
7
  const val = typeof input.val === "string" ? input.val : input.val.get();
7
- return new string_1.String().set(val.repeat(input.occ.get()));
8
+ return new string_1.String().set(val.repeat((0, _position_1.position)(input.occ)));
8
9
  }
9
10
  //# sourceMappingURL=repeat.js.map
@@ -4,6 +4,7 @@ exports.replace = replace;
4
4
  const string_1 = require("../types/string");
5
5
  const abap_regex_1 = require("../abap_regex");
6
6
  const types_1 = require("../types");
7
+ const _position_1 = require("./_position");
7
8
  function dereference(input) {
8
9
  if (input instanceof types_1.FieldSymbol) {
9
10
  const pointer = input.getPointer();
@@ -58,14 +59,12 @@ function replace(input) {
58
59
  else if (regexInput) {
59
60
  sub = new RegExp(abap_regex_1.ABAPRegExp.convert(regexInput.get()), "g");
60
61
  }
61
- if (input.off && input.len && typeof source === "string") {
62
- const offset = input.off.get();
63
- const length = input.len.get();
62
+ const offset = (0, _position_1.position)(input.off);
63
+ const length = (0, _position_1.position)(input.len);
64
+ if (offset !== undefined && length !== undefined && typeof source === "string") {
64
65
  val = val.substring(0, offset) + wi + val.substring(offset + length);
65
66
  }
66
- else if (input.off && input.len && !(typeof source === "string")) {
67
- const offset = input.off.get();
68
- const length = input.len.get();
67
+ else if (offset !== undefined && length !== undefined && !(typeof source === "string")) {
69
68
  val = source.getOffset({ offset: 0, length: offset }).get() +
70
69
  wi +
71
70
  source.getOffset({ offset: offset + length }).get();
@@ -76,7 +75,7 @@ function replace(input) {
76
75
  }
77
76
  val = val.replace(sub, wi);
78
77
  }
79
- else if (input.occ && input.occ.get() === 0 && sub && wi !== undefined) {
78
+ else if (input.occ && (0, _position_1.position)(input.occ) === 0 && sub && wi !== undefined) {
80
79
  if (typeof sub === "string") {
81
80
  sub = new RegExp(sub, "g");
82
81
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.segment = segment;
4
4
  const throw_error_1 = require("../throw_error");
5
5
  const types_1 = require("../types");
6
+ const _position_1 = require("./_position");
6
7
  function segment(input) {
7
8
  let val = input.val;
8
9
  if (typeof val !== "string") {
@@ -12,10 +13,7 @@ function segment(input) {
12
13
  if (typeof sep !== "string") {
13
14
  sep = sep.get();
14
15
  }
15
- let index = input.index;
16
- if (typeof index !== "number") {
17
- index = index.get();
18
- }
16
+ let index = (0, _position_1.position)(input.index);
19
17
  if (index === 0 || sep.length === 0) {
20
18
  (0, throw_error_1.throwError)("CX_SY_STRG_PAR_VAL");
21
19
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.shift_left = shift_left;
4
4
  const string_1 = require("../types/string");
5
5
  const throw_error_1 = require("../throw_error");
6
+ const _position_1 = require("./_position");
6
7
  function shift_left(input) {
7
8
  let val = typeof input.val === "string" ? input.val : input.val.get();
8
9
  if (input.sub) {
@@ -12,17 +13,14 @@ function shift_left(input) {
12
13
  }
13
14
  }
14
15
  else if (input.places) {
15
- let places = typeof input.places === "string" ? input.places : input.places.get();
16
- if (typeof places === "string") {
17
- places = parseInt(places, 10);
18
- }
16
+ const places = (0, _position_1.position)(input.places);
19
17
  if (places > val.length) {
20
18
  (0, throw_error_1.throwError)("CX_SY_RANGE_OUT_OF_BOUNDS");
21
19
  }
22
20
  val = val.substring(places);
23
21
  }
24
22
  else if (input.circular) {
25
- const leftShifts = input.circular.get() % val.length;
23
+ const leftShifts = (0, _position_1.position)(input.circular) % val.length;
26
24
  val = val.slice(leftShifts) + val.slice(0, leftShifts);
27
25
  }
28
26
  else {
@@ -3,15 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.substring = substring;
4
4
  const string_1 = require("../types/string");
5
5
  const throw_error_1 = require("../throw_error");
6
+ const _position_1 = require("./_position");
6
7
  function substring(input) {
7
- let off = input?.off?.get();
8
+ let off = (0, _position_1.position)(input?.off);
8
9
  if (off === undefined) {
9
10
  off = 0;
10
11
  }
11
12
  else if (off < 0) {
12
13
  (0, throw_error_1.throwError)("CX_SY_RANGE_OUT_OF_BOUNDS");
13
14
  }
14
- const len = input?.len?.get();
15
+ const len = (0, _position_1.position)(input?.len);
15
16
  if (len && len < 0) {
16
17
  (0, throw_error_1.throwError)("CX_SY_RANGE_OUT_OF_BOUNDS");
17
18
  }
@@ -93,21 +93,14 @@ function gt(left, right) {
93
93
  else {
94
94
  r = right.get();
95
95
  }
96
+ // a character operand is converted to the type of the numeric one it is
97
+ // compared with: '0.5' against a float is 0.5, not 0. parse() knows the
98
+ // rules, and an empty string is 0 there too
96
99
  if (typeof l === "string" && typeof r === "number") {
97
- if (l === "") {
98
- l = 0;
99
- }
100
- else {
101
- l = parseInt(l, 10);
102
- }
100
+ l = (0, _parse_1.parse)(l);
103
101
  }
104
102
  else if (typeof l === "number" && typeof r === "string") {
105
- if (r === "") {
106
- r = 0;
107
- }
108
- else {
109
- r = parseInt(r, 10);
110
- }
103
+ r = (0, _parse_1.parse)(r);
111
104
  }
112
105
  if (l === undefined) {
113
106
  return true; // todo, not sure this is correct
@@ -11,6 +11,14 @@ function add(left, right) {
11
11
  if (left instanceof types_1.Integer && right instanceof types_1.Integer) {
12
12
  return new types_1.Integer().set(left.get() + right.get());
13
13
  }
14
+ else if (left instanceof types_1.Float && right instanceof types_1.Float) {
15
+ // Two floats fall through the rest of the chain to exactly this, and
16
+ // float arithmetic is the most common thing there is: the remaining type
17
+ // tests all fail, and then parse() is called on each operand, which for a
18
+ // Float is getRaw(). It sits after the integer branch on purpose, so that
19
+ // integer arithmetic pays nothing for it.
20
+ return new types_1.Float().set(left.getRaw() + right.getRaw());
21
+ }
14
22
  else if (typeof left === "number" && typeof right === "number"
15
23
  && Number.isInteger(left) && Number.isInteger(right)) {
16
24
  return new types_1.Integer().set(left + right);
@@ -20,6 +20,14 @@ function minus(left, right) {
20
20
  if (left instanceof types_1.Integer && right instanceof types_1.Integer) {
21
21
  return new types_1.Integer().set(left.get() - right.get());
22
22
  }
23
+ else if (left instanceof types_1.Float && right instanceof types_1.Float) {
24
+ // Two floats fall through the rest of the chain to exactly this, and
25
+ // float arithmetic is the most common thing there is: the remaining type
26
+ // tests all fail, and then parse() is called on each operand, which for a
27
+ // Float is getRaw(). It sits after the integer branch on purpose, so that
28
+ // integer arithmetic pays nothing for it.
29
+ return new types_1.Float().set(left.getRaw() - right.getRaw());
30
+ }
23
31
  else if (left instanceof types_1.Integer8 || right instanceof types_1.Integer8) {
24
32
  const l = left instanceof types_1.Integer8 ? left.get() : BigInt((0, _parse_1.parse)(left));
25
33
  const r = right instanceof types_1.Integer8 ? right.get() : BigInt((0, _parse_1.parse)(right));
@@ -1,4 +1,4 @@
1
- import { Integer, Integer8 } from "../types";
1
+ import { Float, Integer, Integer8 } from "../types";
2
2
  import { ICharacter } from "../types/_character";
3
3
  import { INumeric } from "../types/_numeric";
4
- export declare function mod(left: INumeric | ICharacter | string | Integer8 | number, right: INumeric | Integer8 | ICharacter | string | number): Integer8 | Integer;
4
+ export declare function mod(left: INumeric | ICharacter | string | Integer8 | number, right: INumeric | Integer8 | ICharacter | string | number): Float | Integer8 | Integer;
@@ -36,12 +36,11 @@ function mod(left, right) {
36
36
  if (val < 0) {
37
37
  val = val * -1;
38
38
  }
39
- // hmm, to handle field symbols?
40
- if (left instanceof types_1.Integer8 || right instanceof types_1.Integer8) {
41
- return new types_1.Integer8().set(val);
42
- }
43
- else {
44
- return new types_1.Integer().set(val);
39
+ // the calculation type of the operands decides: with a float operand the
40
+ // remainder is a float, 2.75 MOD 1 is 0.75 and not 1
41
+ if (left instanceof types_1.Float || right instanceof types_1.Float) {
42
+ return new types_1.Float().set(val);
45
43
  }
44
+ return new types_1.Integer().set(val);
46
45
  }
47
46
  //# sourceMappingURL=mod.js.map
@@ -14,6 +14,14 @@ function multiply(left, right) {
14
14
  const val = left.get() * right.get();
15
15
  return new types_1.Integer().set(val);
16
16
  }
17
+ else if (left instanceof types_1.Float && right instanceof types_1.Float) {
18
+ // Two floats fall through the rest of the chain to exactly this, and
19
+ // float arithmetic is the most common thing there is: the remaining type
20
+ // tests all fail, and then parse() is called on each operand, which for a
21
+ // Float is getRaw(). It sits after the integer branch on purpose, so that
22
+ // integer arithmetic pays nothing for it.
23
+ return new types_1.Float().set(left.getRaw() * right.getRaw());
24
+ }
17
25
  else if (typeof left === "number" && typeof right === "number"
18
26
  && Number.isInteger(left) && Number.isInteger(right)) {
19
27
  const val = left * right;
@@ -4,6 +4,23 @@ exports.createData = createData;
4
4
  const throw_error_1 = require("../throw_error");
5
5
  const types_1 = require("../types");
6
6
  const tableOptions = { withHeader: false, keyType: types_1.TableKeyType.default };
7
+ /** CREATE DATA ... TYPE REF TO <name>, both the static and the dynamic spelling.
8
+ * "REF TO data" is the generic case: the created data object is itself an
9
+ * unbound data reference, so the target points at a DataReference. Anything
10
+ * else names a class or interface. */
11
+ function createDataRefTo(target, name) {
12
+ if (name.toUpperCase() === "DATA") {
13
+ // a DataReference pointing at a DataReference, same shape GET REFERENCE OF
14
+ // a "REF TO data" variable produces - the declared types do not model the
15
+ // nesting, so this mirrors get_reference's cast
16
+ target.assign(new types_1.DataReference(undefined));
17
+ return;
18
+ }
19
+ if (abap.Classes[name.toUpperCase()] === undefined) {
20
+ (0, throw_error_1.throwError)("CX_SY_CREATE_DATA_ERROR");
21
+ }
22
+ target.assign(new abap.types.ABAPObject({ qualifiedName: name, RTTIName: name }));
23
+ }
7
24
  function createData(target, options) {
8
25
  // console.dir(options);
9
26
  if (target instanceof types_1.FieldSymbol) {
@@ -70,10 +87,7 @@ function createData(target, options) {
70
87
  target.assign(new types_1.Integer8());
71
88
  }
72
89
  else if (options.refTo === true) {
73
- if (abap.Classes[options.name.toUpperCase()] === undefined) {
74
- (0, throw_error_1.throwError)("CX_SY_CREATE_DATA_ERROR");
75
- }
76
- target.assign(new abap.types.ABAPObject({ qualifiedName: options.name, RTTIName: options.name }));
90
+ createDataRefTo(target, options.name);
77
91
  }
78
92
  else {
79
93
  (0, throw_error_1.throwError)("CX_SY_CREATE_DATA_ERROR");
@@ -170,6 +184,11 @@ function createData(target, options) {
170
184
  }
171
185
  target.assign(abap.Classes[className][typeName.toLowerCase().trimEnd()].clone());
172
186
  }
187
+ else if (options.refTo === true) {
188
+ // statically written "TYPE REF TO ...", the dynamic "TYPE REF TO (name)"
189
+ // variant is handled in the options.name branch above
190
+ createDataRefTo(target, options.typeName.trimEnd());
191
+ }
173
192
  else {
174
193
  throw new Error("CREATE DATA, unknown type " + options.typeName);
175
194
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * CREATE OBJECT with a DYNAMIC class name, into a reference that has a static
3
+ * type: the class the name resolves to has to be compatible with that type.
4
+ *
5
+ * A system raises CX_SY_CREATE_OBJECT_ERROR when it is not, and it raises it
6
+ * BEFORE the constructor runs - so this is checked on the class, not on an
7
+ * instance. Without the check an unrelated class was simply assigned to the
8
+ * reference and the mismatch surfaced later as a raw javascript TypeError on
9
+ * the first member access, which no CATCH takes: a caller that guards its
10
+ * dynamic CREATE OBJECT the way the ABAP documentation asks for was not
11
+ * guarded at all.
12
+ *
13
+ * `targetType` is the class or interface object of the reference's static
14
+ * type and `targetName` its upper-case name; a generic `REF TO object` target
15
+ * passes both as undefined, and then there is nothing to check.
16
+ */
17
+ export declare function checkCreateObjectType(clas: any, targetType: any, targetName?: string): void;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkCreateObjectType = checkCreateObjectType;
4
+ const throw_error_1 = require("../throw_error");
5
+ /**
6
+ * CREATE OBJECT with a DYNAMIC class name, into a reference that has a static
7
+ * type: the class the name resolves to has to be compatible with that type.
8
+ *
9
+ * A system raises CX_SY_CREATE_OBJECT_ERROR when it is not, and it raises it
10
+ * BEFORE the constructor runs - so this is checked on the class, not on an
11
+ * instance. Without the check an unrelated class was simply assigned to the
12
+ * reference and the mismatch surfaced later as a raw javascript TypeError on
13
+ * the first member access, which no CATCH takes: a caller that guards its
14
+ * dynamic CREATE OBJECT the way the ABAP documentation asks for was not
15
+ * guarded at all.
16
+ *
17
+ * `targetType` is the class or interface object of the reference's static
18
+ * type and `targetName` its upper-case name; a generic `REF TO object` target
19
+ * passes both as undefined, and then there is nothing to check.
20
+ */
21
+ function checkCreateObjectType(clas, targetType, targetName) {
22
+ if (clas === undefined || targetType === undefined) {
23
+ return;
24
+ }
25
+ if (targetType.INTERNAL_TYPE === "CLAS") {
26
+ // the class itself or anything below it
27
+ if (clas === targetType || clas.prototype instanceof targetType) {
28
+ return;
29
+ }
30
+ }
31
+ else if (targetType.INTERNAL_TYPE === "INTF") {
32
+ // the same list the cast checks, and it carries the interfaces a
33
+ // superclass or a composed interface brings along
34
+ const list = clas.IMPLEMENTED_INTERFACES;
35
+ if (list?.some(i => i === targetName) === true) {
36
+ return;
37
+ }
38
+ }
39
+ else {
40
+ // not something this can decide - leave it as it was
41
+ return;
42
+ }
43
+ (0, throw_error_1.throwError)("CX_SY_CREATE_OBJECT_ERROR");
44
+ }
45
+ //# sourceMappingURL=create_object.js.map
@@ -12,6 +12,7 @@ import { find } from "./find";
12
12
  import { collect } from "./collect";
13
13
  import { overlay } from "./overlay";
14
14
  import { cast } from "./cast";
15
+ import { checkCreateObjectType } from "./create_object";
15
16
  import { getBit } from "./get_bit";
16
17
  import { readReport } from "./read_report";
17
18
  import { getReference } from "./get_reference";
@@ -58,6 +59,7 @@ export declare class Statements {
58
59
  assert: typeof assert;
59
60
  assign: typeof assign;
60
61
  cast: typeof cast;
62
+ checkCreateObjectType: typeof checkCreateObjectType;
61
63
  collect: typeof collect;
62
64
  concatenate: typeof concatenate;
63
65
  condense: typeof condense;
@@ -15,6 +15,7 @@ const find_1 = require("./find");
15
15
  const collect_1 = require("./collect");
16
16
  const overlay_1 = require("./overlay");
17
17
  const cast_1 = require("./cast");
18
+ const create_object_1 = require("./create_object");
18
19
  const get_bit_1 = require("./get_bit");
19
20
  const read_report_1 = require("./read_report");
20
21
  const get_reference_1 = require("./get_reference");
@@ -61,6 +62,7 @@ class Statements {
61
62
  assert = assert_1.assert;
62
63
  assign = assign_1.assign;
63
64
  cast = cast_1.cast;
65
+ checkCreateObjectType = create_object_1.checkCreateObjectType;
64
66
  collect = collect_1.collect;
65
67
  concatenate = concatenate_1.concatenate;
66
68
  condense = condense_1.condense;
@@ -110,6 +110,24 @@ async function* loop(table, options) {
110
110
  }
111
111
  const loopController = table.startLoop(loopFrom, loopTo, array);
112
112
  let entered = false;
113
+ // ABAP hands sy-tabix back the way it found it. A LOOP owns sy-tabix only
114
+ // for as long as it runs; leaving it, by ENDLOOP or EXIT or RETURN or an
115
+ // exception, puts back whatever the enclosing loop had. Without that an
116
+ // inner loop, or a method that happens to contain one, silently rewrites
117
+ // the row number the outer loop is standing on, and the outer body reads
118
+ // the inner loop's last index as its own.
119
+ const outerTabix = abap.builtin.sy.get().tabix.get();
120
+ // A hashed table has no row number to report, and ABAP says so by leaving
121
+ // sy-tabix at 0 for the whole loop rather than by inventing a position.
122
+ // The same goes for a loop that reads an index table through a hash
123
+ // secondary key. Handing out 1, 2, 3 there looks helpful and is a lie the
124
+ // caller cannot tell from the truth.
125
+ const usedKey = options?.usingKey === undefined || options.usingKey === "primary_key"
126
+ ? undefined
127
+ : table.getKeyByName(options.usingKey);
128
+ const hasRowNumber = usedKey !== undefined
129
+ ? usedKey.type !== types_1.TableAccessType.hashed
130
+ : !(table instanceof types_1.HashedTable);
113
131
  try {
114
132
  const isStructured = array[0] instanceof types_1.Structure;
115
133
  while (loopController.index < loopController.loopTo) {
@@ -124,7 +142,7 @@ async function* loop(table, options) {
124
142
  continue;
125
143
  }
126
144
  }
127
- abap.builtin.sy.get().tabix.set(loopController.index + 1);
145
+ abap.builtin.sy.get().tabix.set(hasRowNumber ? loopController.index + 1 : 0);
128
146
  entered = true;
129
147
  options?.atLast?.(loopController.index + 1 >= loopController.loopTo);
130
148
  yield current;
@@ -138,6 +156,7 @@ async function* loop(table, options) {
138
156
  finally {
139
157
  table.unregisterLoop(loopController);
140
158
  abap.builtin.sy.get().subrc.set(entered ? 0 : 4);
159
+ abap.builtin.sy.get().tabix.set(outerTabix);
141
160
  }
142
161
  }
143
162
  //# sourceMappingURL=loop.js.map
@@ -22,7 +22,10 @@ class DecFloat34 {
22
22
  this.value = 0;
23
23
  }
24
24
  else if (typeof value === "string") {
25
- this.value = parseFloat(value);
25
+ // get() writes the decimal separator as a comma, and parseFloat stops
26
+ // at one: without this, reading back what this type just wrote gives a
27
+ // truncated number and no error at all
28
+ this.value = parseFloat(value.replace(",", "."));
26
29
  }
27
30
  else if (value instanceof DecFloat34) {
28
31
  this.value = value.getRaw();
@@ -6,7 +6,12 @@ const hex_1 = require("./hex");
6
6
  const xstring_1 = require("./xstring");
7
7
  const integer8_1 = require("./integer8");
8
8
  const decfloat34_1 = require("./decfloat34");
9
- const FLOAT_DIGITS = /^\s*[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)? *$/;
9
+ // A float writes itself out with a comma for the decimal separator, so
10
+ // reading one back has to accept a comma. Both are allowed rather than only
11
+ // the comma: ABAP source literals carry a point, `CONV f( '0.25' )` is
12
+ // everywhere, and a separator that is legal on the way out and illegal on the
13
+ // way in is not a rule, it is a trap.
14
+ const FLOAT_DIGITS = /^\s*[+-]?(\d+[.,]?\d*|[.,]\d+)([eE][+-]?\d+)? *$/;
10
15
  /*
11
16
  function getNumberParts(x: number) {
12
17
  if(isNaN(x)) {
@@ -67,7 +72,7 @@ class Float {
67
72
  if (FLOAT_DIGITS.test(value) === false) {
68
73
  (0, throw_error_1.throwError)("CX_SY_CONVERSION_NO_NUMBER");
69
74
  }
70
- this.value = parseFloat(value);
75
+ this.value = parseFloat(value.replace(",", "."));
71
76
  }
72
77
  else if (value instanceof integer8_1.Integer8) {
73
78
  this.value = Number(value.get());
@@ -7,6 +7,7 @@ export declare const DIGITS: RegExp;
7
7
  export declare const MAX_INTEGER = 2147483647;
8
8
  export declare const MIN_INTEGER = -2147483648;
9
9
  export declare function toInteger(value: string, exception?: boolean): number;
10
+ export declare function roundHalfAwayFromZero(value: number): number;
10
11
  export declare class Integer implements INumeric {
11
12
  private value;
12
13
  private constant;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Integer = exports.MIN_INTEGER = exports.MAX_INTEGER = exports.DIGITS = void 0;
4
4
  exports.toInteger = toInteger;
5
+ exports.roundHalfAwayFromZero = roundHalfAwayFromZero;
5
6
  const throw_error_1 = require("../throw_error");
6
7
  const float_1 = require("./float");
7
8
  const hex_1 = require("./hex");
@@ -29,7 +30,14 @@ function toInteger(value, exception = true) {
29
30
  throw new Error("CONVT_NO_NUMBER");
30
31
  }
31
32
  }
32
- return Math.round(parseFloat(value));
33
+ return roundHalfAwayFromZero(parseFloat(value));
34
+ }
35
+ // ABAP rounds a half away from zero when a float is moved to an integer,
36
+ // on both sides: 0.5 is 1 and -0.5 is -1. Math.round rounds a half towards
37
+ // positive infinity, so -0.5 was 0 (and -0 at that)
38
+ function roundHalfAwayFromZero(value) {
39
+ const rounded = value < 0 ? -Math.round(-value) : Math.round(value);
40
+ return rounded === 0 ? 0 : rounded;
33
41
  }
34
42
  class Integer {
35
43
  value;
@@ -67,7 +75,7 @@ class Integer {
67
75
  throw new Error("Changing constant");
68
76
  }
69
77
  if (typeof value === "number") {
70
- this.value = Math.round(value);
78
+ this.value = roundHalfAwayFromZero(value);
71
79
  }
72
80
  else if (value instanceof Integer) {
73
81
  this.set(value.get());
@@ -82,7 +90,7 @@ class Integer {
82
90
  this.set(Number(value.get()));
83
91
  }
84
92
  else if (value instanceof float_1.Float || value instanceof decfloat34_1.DecFloat34) {
85
- this.set(Math.round(value.getRaw()));
93
+ this.set(roundHalfAwayFromZero(value.getRaw()));
86
94
  }
87
95
  else if (value instanceof hex_1.Hex || value instanceof xstring_1.XString || value instanceof hex_uint8_1.HexUInt8) {
88
96
  const hex = value.get();
@@ -47,7 +47,7 @@ class Integer8 {
47
47
  this.value = BigInt(value);
48
48
  }
49
49
  else if (value instanceof float_1.Float || value instanceof decfloat34_1.DecFloat34) {
50
- this.set(Math.round(value.getRaw()));
50
+ this.set((0, integer_1.roundHalfAwayFromZero)(value.getRaw()));
51
51
  }
52
52
  else if (value instanceof hex_1.Hex || value instanceof xstring_1.XString || value instanceof hex_uint8_1.HexUInt8) {
53
53
  if (value.get().length === 16) {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Table = exports.HashedTable = exports.TableFactory = exports.LoopController = exports.TableKeyType = exports.TableAccessType = void 0;
4
4
  const integer_1 = require("./integer");
5
+ const float_1 = require("./float");
5
6
  const abap_object_1 = require("./abap_object");
6
7
  const string_1 = require("./string");
7
8
  const structure_1 = require("./structure");
@@ -294,8 +295,12 @@ class HashedTable {
294
295
  cloneRow(item) {
295
296
  // make sure to do conversion if needed
296
297
  if (typeof item === "number") {
298
+ // a raw number is what the numeric built-ins return (sin, cos, sqrt,
299
+ // abs, floor, ...): a whole one is an integer, anything else is a
300
+ // float, and wrapping every one as an Integer first rounded a sine
301
+ // table to zeros before the row ever saw the value
297
302
  const tmp = this.getRowType().clone();
298
- tmp.set(new integer_1.Integer().set(item));
303
+ tmp.set(Number.isInteger(item) ? new integer_1.Integer().set(item) : new float_1.Float().set(item));
299
304
  return tmp;
300
305
  }
301
306
  else if (typeof item === "string") {
@@ -564,8 +569,12 @@ class Table {
564
569
  cloneRow(item) {
565
570
  // make sure to do conversion if needed
566
571
  if (typeof item === "number") {
572
+ // a raw number is what the numeric built-ins return (sin, cos, sqrt,
573
+ // abs, floor, ...): a whole one is an integer, anything else is a
574
+ // float, and wrapping every one as an Integer first rounded a sine
575
+ // table to zeros before the row ever saw the value
567
576
  const tmp = this.getRowType().clone();
568
- tmp.set(new integer_1.Integer().set(item));
577
+ tmp.set(Number.isInteger(item) ? new integer_1.Integer().set(item) : new float_1.Float().set(item));
569
578
  return tmp;
570
579
  }
571
580
  else if (typeof item === "string") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/runtime",
3
- "version": "2.13.86",
3
+ "version": "2.13.88",
4
4
  "description": "Transpiler - Runtime",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/src/index.d.ts",
@@ -33,7 +33,7 @@
33
33
  "@types/mocha": "^10.0.10",
34
34
  "@types/node": "^26.4.1",
35
35
  "chai": "^6.2.2",
36
- "mocha": "^12.0.1",
36
+ "mocha": "^12.0.2",
37
37
  "source-map-support": "^0.5.21",
38
38
  "typescript": "^6.0.3"
39
39
  },