@xano/xanoscript-language-server 11.12.1 → 11.12.2

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.
@@ -0,0 +1,84 @@
1
+ import { expect } from "chai";
2
+ import { describe, it } from "mocha";
3
+ import { lexDocument } from "./lexer.js";
4
+
5
+ function getTokenTypes(text) {
6
+ const result = lexDocument(text);
7
+ return {
8
+ tokens: result.tokens.map((t) => ({
9
+ type: t.tokenType.name,
10
+ image: t.image,
11
+ })),
12
+ errors: result.errors,
13
+ };
14
+ }
15
+
16
+ describe("RegExpToken lexer", () => {
17
+ it("should not lex a standalone regex without preceding colon", () => {
18
+ const { tokens } = getTokenTypes("/[^213]*/");
19
+ const regexpTokens = tokens.filter((t) => t.type === "RegExpToken");
20
+ expect(regexpTokens).to.have.length(0);
21
+ });
22
+
23
+ it("should lex a regex after a colon", () => {
24
+ const { tokens, errors } = getTokenTypes("pattern:/[^213]*/");
25
+ expect(errors).to.be.empty;
26
+ const regexpTokens = tokens.filter((t) => t.type === "RegExpToken");
27
+ expect(regexpTokens).to.have.length(1);
28
+ expect(regexpTokens[0].image).to.equal("/[^213]*/");
29
+ });
30
+
31
+ it("should lex / as divide after an identifier", () => {
32
+ const { tokens } = getTokenTypes("var_rel/55");
33
+ const divideTokens = tokens.filter((t) => t.type === "Divide");
34
+ expect(divideTokens).to.have.length(1);
35
+ const regexpTokens = tokens.filter((t) => t.type === "RegExpToken");
36
+ expect(regexpTokens).to.have.length(0);
37
+ });
38
+
39
+ it("should lex / as divide after a number", () => {
40
+ const { tokens } = getTokenTypes("55/2");
41
+ const divideTokens = tokens.filter((t) => t.type === "Divide");
42
+ expect(divideTokens).to.have.length(1);
43
+ const regexpTokens = tokens.filter((t) => t.type === "RegExpToken");
44
+ expect(regexpTokens).to.have.length(0);
45
+ });
46
+
47
+ it("should lex / as divide after closing paren", () => {
48
+ const { tokens } = getTokenTypes("(1+2)/3");
49
+ const divideTokens = tokens.filter((t) => t.type === "Divide");
50
+ expect(divideTokens).to.have.length(1);
51
+ });
52
+
53
+ it("should not capture division as regex in complex expressions", () => {
54
+ const { tokens, errors } = getTokenTypes("$SomeVar.0.var_rel/55");
55
+ expect(errors).to.be.empty;
56
+ const regexpTokens = tokens.filter((t) => t.type === "RegExpToken");
57
+ expect(regexpTokens).to.have.length(0);
58
+ const divideTokens = tokens.filter((t) => t.type === "Divide");
59
+ expect(divideTokens).to.have.length(1);
60
+ });
61
+
62
+ it("should lex / as divide with spaces around it", () => {
63
+ const { tokens, errors } = getTokenTypes("$var /64");
64
+ expect(errors).to.be.empty;
65
+ const regexpTokens = tokens.filter((t) => t.type === "RegExpToken");
66
+ expect(regexpTokens).to.have.length(0);
67
+ const divideTokens = tokens.filter((t) => t.type === "Divide");
68
+ expect(divideTokens).to.have.length(1);
69
+ });
70
+
71
+ it("should lex / as divide with space only before it", () => {
72
+ const { tokens, errors } = getTokenTypes("$var/ 64");
73
+ expect(errors).to.be.empty;
74
+ const divideTokens = tokens.filter((t) => t.type === "Divide");
75
+ expect(divideTokens).to.have.length(1);
76
+ });
77
+
78
+ it("should lex / as divide when no spaces around a value", () => {
79
+ const { tokens, errors } = getTokenTypes("$var /64/ 34");
80
+ expect(errors).to.be.empty;
81
+ const divideTokens = tokens.filter((t) => t.type === "Divide");
82
+ expect(divideTokens).to.have.length(2);
83
+ });
84
+ });
package/lexer/tokens.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Lexer } from "chevrotain";
1
+ import { createToken as chevrotainCreateToken, Lexer } from "chevrotain";
2
2
  import { Identifier } from "./identifier.js";
3
3
  import {
4
4
  createTokenByName,
@@ -150,10 +150,29 @@ export const NullToken = createTokenByName("null", {
150
150
  });
151
151
 
152
152
  // regular expression (regexp) pattern
153
- export const RegExpToken = createToken({
153
+ // A regexp literal only appears after a colon (e.g., filter:pattern:/[^abc]*/)
154
+ const regexpPattern = /\/(?:[^/\n\\ ]|\\.)(?:[^/\n\\]|\\.)*\//y;
155
+ function matchRegExp(text, startOffset, tokens) {
156
+ if (
157
+ tokens.length === 0 ||
158
+ tokens[tokens.length - 1].tokenType.name !== "ColonToken"
159
+ ) {
160
+ return null;
161
+ }
162
+ regexpPattern.lastIndex = startOffset;
163
+ const result = regexpPattern.exec(text);
164
+ if (result) {
165
+ return [result[0]];
166
+ }
167
+ return null;
168
+ }
169
+
170
+ export const RegExpToken = chevrotainCreateToken({
154
171
  name: "RegExpToken",
155
172
  label: "regexp",
156
- pattern: /\/(?:[^/\n\\ ]|\\.)(?:[^/\n\\]|\\.)*\//,
173
+ pattern: { exec: matchRegExp },
174
+ line_breaks: false,
175
+ start_chars_hint: ["/"],
157
176
  });
158
177
 
159
178
  export const WhiteSpace = createToken({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xano/xanoscript-language-server",
3
- "version": "11.12.1",
3
+ "version": "11.12.2",
4
4
  "description": "Language Server Protocol implementation for XanoScript",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -26,6 +26,16 @@ describe("functionRunFn", () => {
26
26
  expect(parser.errors).to.be.empty;
27
27
  });
28
28
 
29
+ it("functionRunFn accepts complex expression as input", () => {
30
+ const parser = parse(`function.run "my_complex_function" {
31
+ input = {
32
+ Snumber : (($SomeVar.0.var_rel/55) > 0 ? ($SomeVar.0.var_rel1/61) : 6)+(($My_Var_S2.0.elo/ 60) > 7 ? ($My_Var_S2.0.elo/62) : 9)+(($Optional_var2.0.det/63) > 4 ? ($Optional_var2.0.det /64) : 2)+(($PtSgeS_sge.0.elo/65) > 1 ? ($PtSgeS_sge.0.elo/66) : 3)
33
+ NumberObject: []
34
+ }
35
+ } as $Var_Round`);
36
+ expect(parser.errors).to.be.empty;
37
+ });
38
+
29
39
  it("functionRunFn accepts a string for function name", () => {
30
40
  const parser = parse(`function.run "my/function" {
31
41
  input = {a: $input.a, b: $b}
@@ -1,55 +0,0 @@
1
- import { expect } from "chai";
2
- import { readdirSync, readFileSync } from "fs";
3
- import { truncate } from "lodash-es";
4
- import { describe, it } from "mocha";
5
- import { dirname, join } from "path";
6
- import { multidocParser } from "../../multidoc.js";
7
-
8
- describe("Xanoscript multidoc Parser", () => {
9
- it("parses all the valid sources", () => {
10
- const errors = [];
11
- const files = readdirSync(
12
- join(dirname("./"), "parser/tests/multidoc/valid_sources"),
13
- );
14
-
15
- files.forEach((file) => {
16
- const source = readFileSync(
17
- join(dirname("./"), "parser/tests/multidoc/valid_sources", file),
18
- "utf8",
19
- );
20
- const parser = multidocParser(source);
21
-
22
- if (parser.errors.length > 0) {
23
- parser.errors.forEach((error) => {
24
- errors.push({
25
- previous: error.previousToken
26
- ? `${error.previousToken.tokenTypeName} (${truncate(
27
- error.previousToken.image,
28
- {
29
- length: 20,
30
- },
31
- )})`
32
- : "N/A",
33
- file: error.token
34
- ? `parser/tests/multidoc/valid_sources/${file}:${error.token.startLine}:${error.token.startColumn}`
35
- : `parser/tests/multidoc/valid_sources/${file}`,
36
- stack:
37
- parser.sectionStack?.[parser.sectionStack.length - 1] ?? "N/A",
38
- message: truncate(error.message, { length: 50 }),
39
- token: error.token
40
- ? `${error.token.tokenTypeName} (${truncate(error.token.image, {
41
- length: 20,
42
- })})`
43
- : "N/A",
44
- });
45
- });
46
- }
47
- });
48
-
49
- if (errors.length > 0) {
50
- console.table(errors);
51
- }
52
-
53
- expect(errors).to.be.empty;
54
- });
55
- });