@abaplint/core 2.119.59 → 2.119.60

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.
@@ -12,6 +12,37 @@ const ModeStr = 3;
12
12
  const ModeTemplate = 4;
13
13
  const ModeComment = 5;
14
14
  const ModePragma = 6;
15
+ // character codes, used for integer comparisons in the hot loop
16
+ const EOF = -1; // no character (start/end of file)
17
+ const CH_TAB = 9; // "\t"
18
+ const CH_NL = 10; // "\n"
19
+ const CH_SPACE = 32; // " "
20
+ const CH_DQUOTE = 34; // "\""
21
+ const CH_HASH = 35; // "#"
22
+ const CH_QUOTE = 39; // "'"
23
+ const CH_STAR = 42; // "*"
24
+ const CH_DASH = 45; // "-"
25
+ const CH_COMMA = 44; // ","
26
+ const CH_DOT = 46; // "."
27
+ const CH_COLON = 58; // ":"
28
+ const CH_EQUALS = 61; // "="
29
+ const CH_GT = 62; // ">"
30
+ const CH_AT = 64; // "@"
31
+ const CH_BACKSLASH = 92; // "\\"
32
+ const CH_BACKTICK = 96; // "`"
33
+ const CH_LBRACE = 123; // "{"
34
+ const CH_PIPE = 124; // "|"
35
+ const CH_RBRACE = 125; // "}"
36
+ // characters that terminate the current token
37
+ const SPLITS = new Set([
38
+ CH_SPACE, CH_COLON, CH_DOT, CH_COMMA, CH_DASH, 43 /* + */, 40 /* ( */,
39
+ 41 /* ) */, 91 /* [ */, 93 /* ] */, CH_BACKSLASH, CH_TAB, CH_NL
40
+ ]);
41
+ // single characters that are emitted as their own token
42
+ const BUFS = new Set([
43
+ CH_DOT, CH_COMMA, CH_COLON, 40 /* ( */, 41 /* ) */, 91 /* [ */,
44
+ 93 /* ] */, 43 /* + */, CH_AT
45
+ ]);
15
46
  class Lexer {
16
47
  run(file, virtual) {
17
48
  this.virtual = virtual;
@@ -26,15 +57,17 @@ class Lexer {
26
57
  const col = this.stream.getCol();
27
58
  const row = this.stream.getRow();
28
59
  let whiteBefore = false;
29
- if (this.stream.getOffset() - s.length >= 0) {
30
- const prev = this.stream.getRaw().substr(this.stream.getOffset() - s.length, 1);
31
- if (prev === " " || prev === "\n" || prev === "\t" || prev === ":") {
60
+ const beforeOffset = this.stream.getOffset() - s.length;
61
+ if (beforeOffset >= 0) {
62
+ const prev = this.stream.charCodeAt(beforeOffset);
63
+ if (prev === CH_SPACE || prev === CH_NL || prev === CH_TAB || prev === CH_COLON) {
32
64
  whiteBefore = true;
33
65
  }
34
66
  }
35
67
  let whiteAfter = false;
36
68
  const next = this.stream.nextChar();
37
- if (next === " " || next === "\n" || next === "\t" || next === ":" || next === "," || next === "." || next === "" || next === "\"") {
69
+ if (next === CH_SPACE || next === CH_NL || next === CH_TAB || next === CH_COLON
70
+ || next === CH_COMMA || next === CH_DOT || next === EOF || next === CH_DQUOTE) {
38
71
  whiteAfter = true;
39
72
  }
40
73
  let pos = new position_1.Position(row, col - s.length);
@@ -204,10 +237,10 @@ class Lexer {
204
237
  }
205
238
  }
206
239
  if (tok === undefined && this.m === ModeNormal && s.charAt(0) === "\\") {
207
- const adj = this.stream.nextChar() === "" ? 1 : 0;
240
+ const adj = this.stream.nextChar() === EOF ? 1 : 0;
208
241
  const prevOffset = this.stream.getOffset() - s.length - adj;
209
- const prevChar = prevOffset >= 0 ? this.stream.getRaw().substr(prevOffset, 1) : "";
210
- const whiteBeforeBackslash = prevChar === " " || prevChar === "\n" || prevChar === "\t" || prevChar === ":";
242
+ const prevChar = this.stream.charCodeAt(prevOffset);
243
+ const whiteBeforeBackslash = prevChar === CH_SPACE || prevChar === CH_NL || prevChar === CH_TAB || prevChar === CH_COLON;
211
244
  if (!whiteBeforeBackslash) {
212
245
  tok = new tokens_1.AssociationName(pos, s);
213
246
  }
@@ -220,100 +253,77 @@ class Lexer {
220
253
  this.buffer.clear();
221
254
  }
222
255
  process(raw) {
223
- this.stream = new lexer_stream_1.LexerStream(raw.replace(/\r/g, ""));
224
- this.buffer = new lexer_buffer_1.LexerBuffer();
225
- const splits = {};
226
- splits[" "] = true;
227
- splits[":"] = true;
228
- splits["."] = true;
229
- splits[","] = true;
230
- splits["-"] = true;
231
- splits["+"] = true;
232
- splits["("] = true;
233
- splits[")"] = true;
234
- splits["["] = true;
235
- splits["]"] = true;
236
- splits["\\"] = true;
237
- splits["\t"] = true;
238
- splits["\n"] = true;
239
- const bufs = {};
240
- bufs["."] = true;
241
- bufs[","] = true;
242
- bufs[":"] = true;
243
- bufs["("] = true;
244
- bufs[")"] = true;
245
- bufs["["] = true;
246
- bufs["]"] = true;
247
- bufs["+"] = true;
248
- bufs["@"] = true;
256
+ const stream = new lexer_stream_1.LexerStream(raw.replace(/\r/g, ""));
257
+ this.stream = stream;
258
+ this.buffer = new lexer_buffer_1.LexerBuffer(stream.getRaw());
249
259
  for (;;) {
250
- const current = this.stream.currentChar();
251
- const buf = this.buffer.add(current);
252
- const ahead = this.stream.nextChar();
253
- const aahead = this.stream.nextNextChar();
260
+ const current = stream.currentChar();
261
+ this.buffer.add(stream.getOffset());
262
+ const ahead = stream.nextChar();
263
+ const aahead = stream.nextNextChar();
254
264
  if (this.m === ModeNormal) {
255
- if (splits[ahead]) {
265
+ if (SPLITS.has(ahead)) {
256
266
  this.add();
257
267
  }
258
- else if (ahead === "'") {
268
+ else if (ahead === CH_QUOTE) {
259
269
  // start string
260
270
  this.add();
261
271
  this.m = ModeStr;
262
272
  }
263
- else if (ahead === "|" || ahead === "}") {
273
+ else if (ahead === CH_PIPE || ahead === CH_RBRACE) {
264
274
  // start template
265
275
  this.add();
266
276
  this.m = ModeTemplate;
267
277
  }
268
- else if (ahead === "`") {
278
+ else if (ahead === CH_BACKTICK) {
269
279
  // start ping
270
280
  this.add();
271
281
  this.m = ModePing;
272
282
  }
273
- else if (aahead === "##") {
283
+ else if (ahead === CH_HASH && aahead === CH_HASH) {
274
284
  // start pragma
275
285
  this.add();
276
286
  this.m = ModePragma;
277
287
  }
278
- else if (ahead === "\""
279
- || (ahead === "*" && current === "\n")) {
288
+ else if (ahead === CH_DQUOTE
289
+ || (ahead === CH_STAR && current === CH_NL)) {
280
290
  // start comment
281
291
  this.add();
282
292
  this.m = ModeComment;
283
293
  }
284
- else if (ahead === "@" && buf.trim().length === 0) {
294
+ else if (ahead === CH_AT && this.buffer.get().trim().length === 0) {
285
295
  this.add();
286
296
  }
287
- else if (aahead === "->"
288
- || aahead === "=>") {
297
+ else if ((ahead === CH_DASH || ahead === CH_EQUALS) && aahead === CH_GT) {
289
298
  this.add();
290
299
  }
291
- else if (current === ">"
292
- && ahead !== " "
293
- && (this.stream.prevChar() === "-" || this.stream.prevChar() === "=")) {
300
+ else if (current === CH_GT
301
+ && ahead !== CH_SPACE
302
+ && (stream.prevChar() === CH_DASH || stream.prevChar() === CH_EQUALS)) {
294
303
  // arrows
295
304
  this.add();
296
305
  }
297
- else if (buf.length === 1
298
- && (bufs[buf]
299
- || (buf === "-" && ahead !== ">"))) {
306
+ else if (this.buffer.length() === 1
307
+ && (BUFS.has(current)
308
+ || (current === CH_DASH && ahead !== CH_GT))) {
300
309
  this.add();
301
310
  }
302
311
  }
303
- else if (this.m === ModePragma && (ahead === "," || ahead === ":" || ahead === "." || ahead === " " || ahead === "\n")) {
312
+ else if (this.m === ModePragma && (ahead === CH_COMMA || ahead === CH_COLON
313
+ || ahead === CH_DOT || ahead === CH_SPACE || ahead === CH_NL)) {
304
314
  // end of pragma
305
315
  this.add();
306
316
  this.m = ModeNormal;
307
317
  }
308
318
  else if (this.m === ModePing
309
- && buf.length > 1
310
- && current === "`"
311
- && aahead !== "``"
312
- && ahead !== "`"
313
- && this.buffer.countIsEven("`")) {
319
+ && this.buffer.length() > 1
320
+ && current === CH_BACKTICK
321
+ && !(ahead === CH_BACKTICK && aahead === CH_BACKTICK)
322
+ && ahead !== CH_BACKTICK
323
+ && this.buffer.countIsEven(CH_BACKTICK)) {
314
324
  // end of ping
315
325
  this.add();
316
- if (ahead === `"`) {
326
+ if (ahead === CH_DQUOTE) {
317
327
  this.m = ModeComment;
318
328
  }
319
329
  else {
@@ -321,41 +331,41 @@ class Lexer {
321
331
  }
322
332
  }
323
333
  else if (this.m === ModeTemplate
324
- && buf.length > 1
325
- && (current === "|" || current === "{")
326
- && (this.stream.prevChar() !== "\\" || this.stream.prevPrevChar() === "\\\\")) {
334
+ && this.buffer.length() > 1
335
+ && (current === CH_PIPE || current === CH_LBRACE)
336
+ && (stream.prevChar() !== CH_BACKSLASH || (stream.prevPrevChar() === CH_BACKSLASH && stream.prevChar() === CH_BACKSLASH))) {
327
337
  // end of template
328
338
  this.add();
329
339
  this.m = ModeNormal;
330
340
  }
331
341
  else if (this.m === ModeTemplate
332
- && ahead === "}"
333
- && current !== "\\") {
342
+ && ahead === CH_RBRACE
343
+ && current !== CH_BACKSLASH) {
334
344
  this.add();
335
345
  }
336
346
  else if (this.m === ModeStr
337
- && current === "'"
338
- && buf.length > 1
339
- && aahead !== "''"
340
- && ahead !== "'"
341
- && this.buffer.countIsEven("'")) {
347
+ && current === CH_QUOTE
348
+ && this.buffer.length() > 1
349
+ && !(ahead === CH_QUOTE && aahead === CH_QUOTE)
350
+ && ahead !== CH_QUOTE
351
+ && this.buffer.countIsEven(CH_QUOTE)) {
342
352
  // end of string
343
353
  this.add();
344
- if (ahead === "\"") {
354
+ if (ahead === CH_DQUOTE) {
345
355
  this.m = ModeComment;
346
356
  }
347
357
  else {
348
358
  this.m = ModeNormal;
349
359
  }
350
360
  }
351
- else if (ahead === "\n" && this.m !== ModeTemplate) {
361
+ else if (ahead === CH_NL && this.m !== ModeTemplate) {
352
362
  this.add();
353
363
  this.m = ModeNormal;
354
364
  }
355
- else if (this.m === ModeTemplate && current === "\n") {
365
+ else if (this.m === ModeTemplate && current === CH_NL) {
356
366
  this.add();
357
367
  }
358
- if (!this.stream.advance()) {
368
+ if (!stream.advance()) {
359
369
  break;
360
370
  }
361
371
  }
@@ -1,24 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LexerBuffer = void 0;
4
+ // The buffer holds a reference to the raw input and tracks the [start, end)
5
+ // offset range of the current token, so the token string can be sliced out with
6
+ // a single substring() instead of being concatenated one character at a time.
4
7
  class LexerBuffer {
5
- constructor() {
6
- this.buf = "";
8
+ constructor(raw) {
9
+ this.raw = raw;
10
+ this.start = 0;
11
+ this.end = 0;
12
+ this.empty = true;
7
13
  }
8
- add(s) {
9
- this.buf = this.buf + s;
10
- return this.buf;
14
+ add(offset) {
15
+ if (this.empty === true) {
16
+ this.start = offset < 0 ? 0 : offset;
17
+ this.empty = false;
18
+ }
19
+ this.end = offset + 1;
11
20
  }
12
21
  get() {
13
- return this.buf;
22
+ return this.raw.substring(this.start, this.end);
23
+ }
24
+ length() {
25
+ return this.end - this.start;
14
26
  }
15
27
  clear() {
16
- this.buf = "";
28
+ this.start = 0;
29
+ this.end = 0;
30
+ this.empty = true;
17
31
  }
18
32
  countIsEven(char) {
19
33
  let count = 0;
20
- for (let i = 0; i < this.buf.length; i += 1) {
21
- if (this.buf.charAt(i) === char) {
34
+ for (let i = this.start; i < this.end; i += 1) {
35
+ if (this.raw.charCodeAt(i) === char) {
22
36
  count += 1;
23
37
  }
24
38
  }
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LexerStream = void 0;
4
+ const NL = 10; // "\n"
5
+ const EOF = -1; // no character (start/end of file)
4
6
  class LexerStream {
5
7
  constructor(raw) {
6
8
  this.offset = -1;
@@ -9,7 +11,7 @@ class LexerStream {
9
11
  this.col = 0;
10
12
  }
11
13
  advance() {
12
- if (this.currentChar() === "\n") {
14
+ if (this.currentChar() === NL) {
13
15
  this.col = 1;
14
16
  this.row = this.row + 1;
15
17
  }
@@ -27,38 +29,51 @@ class LexerStream {
27
29
  getRow() {
28
30
  return this.row;
29
31
  }
32
+ // the *Char() accessors return character codes (charCodeAt) rather than
33
+ // single character strings, to avoid allocating a string per input character
34
+ // in the lexer hot loop. EOF (-1) is returned when the offset is out of range.
30
35
  prevChar() {
31
- if (this.offset - 1 < 0) {
32
- return "";
36
+ const o = this.offset - 1;
37
+ if (o < 0) {
38
+ return EOF;
33
39
  }
34
- return this.raw.substr(this.offset - 1, 1);
40
+ return this.raw.charCodeAt(o);
35
41
  }
36
42
  prevPrevChar() {
37
- if (this.offset - 2 < 0) {
38
- return "";
43
+ const o = this.offset - 2;
44
+ if (o < 0) {
45
+ return EOF;
39
46
  }
40
- return this.raw.substr(this.offset - 2, 2);
47
+ return this.raw.charCodeAt(o);
41
48
  }
42
49
  currentChar() {
43
50
  if (this.offset < 0) {
44
- return "\n"; // simulate newline at start of file to handle star(*) comments
51
+ return NL; // simulate newline at start of file to handle star(*) comments
45
52
  }
46
53
  else if (this.offset >= this.raw.length) {
47
- return "";
54
+ return EOF;
48
55
  }
49
- return this.raw.substr(this.offset, 1);
56
+ return this.raw.charCodeAt(this.offset);
50
57
  }
51
58
  nextChar() {
52
- if (this.offset + 2 > this.raw.length) {
53
- return "";
59
+ const o = this.offset + 1;
60
+ if (o >= this.raw.length) {
61
+ return EOF;
54
62
  }
55
- return this.raw.substr(this.offset + 1, 1);
63
+ return this.raw.charCodeAt(o);
56
64
  }
57
65
  nextNextChar() {
58
- if (this.offset + 3 > this.raw.length) {
59
- return this.nextChar();
66
+ const o = this.offset + 2;
67
+ if (o >= this.raw.length) {
68
+ return EOF;
60
69
  }
61
- return this.raw.substr(this.offset + 1, 2);
70
+ return this.raw.charCodeAt(o);
71
+ }
72
+ charCodeAt(o) {
73
+ if (o < 0 || o >= this.raw.length) {
74
+ return EOF;
75
+ }
76
+ return this.raw.charCodeAt(o);
62
77
  }
63
78
  getRaw() {
64
79
  return this.raw;
@@ -123,8 +123,9 @@ class Word {
123
123
  }
124
124
  }
125
125
  class Token {
126
- constructor(s) {
127
- this.name = s;
126
+ constructor(t) {
127
+ this.tokenType = t;
128
+ this.name = t.name;
128
129
  }
129
130
  listKeywords() {
130
131
  return [];
@@ -136,7 +137,7 @@ class Token {
136
137
  const result = [];
137
138
  for (const input of r) {
138
139
  if (input.remainingLength() !== 0
139
- && input.peek().constructor.name === this.name) {
140
+ && input.peek().constructor === this.tokenType) {
140
141
  result.push(input.shift(new nodes_1.TokenNode(input.peek())));
141
142
  }
142
143
  }
@@ -332,7 +333,12 @@ class Optional {
332
333
  for (const input of r) {
333
334
  result.push(input);
334
335
  const res = this.optional.run([input]);
335
- result.push(...res);
336
+ if (res.length === 1) {
337
+ result.push(res[0]);
338
+ }
339
+ else {
340
+ result.push(...res);
341
+ }
336
342
  }
337
343
  return result;
338
344
  }
@@ -371,6 +377,9 @@ class Star {
371
377
  // avoid stack overflow
372
378
  result = result.concat(res);
373
379
  }
380
+ else if (res.length === 1) {
381
+ result.push(res[0]);
382
+ }
374
383
  else {
375
384
  result.push(...res);
376
385
  }
@@ -522,6 +531,9 @@ class Sequence {
522
531
  // avoid stack overflow
523
532
  result = result.concat(temp);
524
533
  }
534
+ else if (temp.length === 1) {
535
+ result.push(temp[0]);
536
+ }
525
537
  else {
526
538
  result.push(...temp);
527
539
  }
@@ -746,7 +758,12 @@ class Alternative {
746
758
  const result = [];
747
759
  for (const sequ of this.list) {
748
760
  const temp = sequ.run(r);
749
- result.push(...temp);
761
+ if (temp.length === 1) {
762
+ result.push(temp[0]);
763
+ }
764
+ else {
765
+ result.push(...temp);
766
+ }
750
767
  }
751
768
  return result;
752
769
  }
@@ -804,7 +821,12 @@ class AlternativePriority {
804
821
  // console.log(seq.toStr());
805
822
  const temp = sequ.run(r);
806
823
  if (temp.length > 0) {
807
- result.push(...temp);
824
+ if (temp.length === 1) {
825
+ result.push(temp[0]);
826
+ }
827
+ else {
828
+ result.push(...temp);
829
+ }
808
830
  break;
809
831
  }
810
832
  }
@@ -920,7 +942,7 @@ function regex(r) {
920
942
  return new Regex(r);
921
943
  }
922
944
  function tok(t) {
923
- return new Token(t.name);
945
+ return new Token(t);
924
946
  }
925
947
  const expressionSingletons = {};
926
948
  const stringSingletons = {};
@@ -75,7 +75,7 @@ class Registry {
75
75
  }
76
76
  static abaplintVersion() {
77
77
  // magic, see build script "version.js"
78
- return "2.119.59";
78
+ return "2.119.60";
79
79
  }
80
80
  getDDICReferences() {
81
81
  return this.ddicReferences;
@@ -59,6 +59,7 @@ class DangerousStatementConf extends _basic_rule_config_1.BasicRuleConfig {
59
59
  this.insertTextpool = true;
60
60
  this.deleteDynpro = true;
61
61
  this.exportDynpro = true;
62
+ this.editorCallForReport = true;
62
63
  /** Finds instances of dynamic SQL: SELECT, UPDATE, DELETE, INSERT, MODIFY */
63
64
  this.dynamicSQL = true;
64
65
  /** Ignore dynamic SQL in IF_RAP_QUERY_PROVIDER~SELECT implementations */
@@ -143,6 +144,11 @@ dynamic SQL can potentially create SQL injection problems`,
143
144
  else if (this.conf.exportDynpro && statement instanceof Statements.ExportDynpro) {
144
145
  message = "EXPORT DYNPRO";
145
146
  }
147
+ else if (this.conf.editorCallForReport
148
+ && statement instanceof Statements.EditorCall
149
+ && statementNode.findDirectTokenByText("REPORT")) {
150
+ message = "EDITOR-CALL FOR REPORT";
151
+ }
146
152
  if (message) {
147
153
  issues.push(issue_1.Issue.atStatement(file, statementNode, this.getDescription(message), this.getMetadata().key, this.conf.severity));
148
154
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/core",
3
- "version": "2.119.59",
3
+ "version": "2.119.60",
4
4
  "description": "abaplint - Core API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/abaplint.d.ts",