@lango-project/lango-js 0.1.3 → 0.1.5

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/compiler.mjs +53 -34
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lango-project/lango-js",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "license": "Apache-2.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/src/compiler.mjs CHANGED
@@ -557,6 +557,8 @@ class Parser {
557
557
  } else if (/^global (\$?[A-Za-z_]\w*)$/.test(text)) {
558
558
  const [, name] = /^global (\$?[A-Za-z_]\w*)$/.exec(text);
559
559
  body.push({ type: "globalDeclare", name, line: line.number });
560
+ } else if (/^@{1,2}\$?[A-Za-z_]\w*.*\(.*\)$/.test(text) && !text.includes(" = ")) {
561
+ body.push({ type: "expression", value: expr(text, line, this) });
560
562
  } else if (/^context ([A-Za-z_]\w*) = (.+)$/.test(text)) {
561
563
  const [, name, value] = /^context ([A-Za-z_]\w*) = (.+)$/.exec(text);
562
564
  body.push({ type: "contextSet", name, value: expr(value, line, this) });
@@ -944,9 +946,9 @@ class Parser {
944
946
  name,
945
947
  value: { type: "sql", text: sqlText, parts, parameters },
946
948
  });
947
- } else if (/^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\]\[([^\]]+)\] = (.+)$/.test(text)) {
949
+ } else if (/^(@{0,2}\$?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\]\[([^\]]+)\] = (.+)$/.test(text)) {
948
950
  const [, receiver, firstIndex, secondIndex, sourceValue] =
949
- /^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\]\[([^\]]+)\] = (.+)$/.exec(text);
951
+ /^(@{0,2}\$?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\]\[([^\]]+)\] = (.+)$/.exec(text);
950
952
  body.push({
951
953
  type: "indexAssign",
952
954
  receiver,
@@ -954,9 +956,9 @@ class Parser {
954
956
  value: this.expression(sourceValue, line),
955
957
  operator: null,
956
958
  });
957
- } else if (/^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\]\[([^\]]+)\] (\+|-|\*|\/|%|\*\*)= (.+)$/.test(text)) {
959
+ } else if (/^(@{0,2}\$?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\]\[([^\]]+)\] (\+|-|\*|\/|%|\*\*)= (.+)$/.test(text)) {
958
960
  const [, receiver, firstIndex, secondIndex, operator, sourceValue] =
959
- /^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\]\[([^\]]+)\] (\+|-|\*|\/|%|\*\*)= (.+)$/.exec(text);
961
+ /^(@{0,2}\$?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\]\[([^\]]+)\] (\+|-|\*|\/|%|\*\*)= (.+)$/.exec(text);
960
962
  body.push({
961
963
  type: "indexAssign",
962
964
  receiver,
@@ -964,9 +966,9 @@ class Parser {
964
966
  value: this.expression(sourceValue, line),
965
967
  operator,
966
968
  });
967
- } else if (/^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\]\[([^\]]+)\](\+\+|--)$/.test(text)) {
969
+ } else if (/^(@{0,2}\$?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\]\[([^\]]+)\](\+\+|--)$/.test(text)) {
968
970
  const [, receiver, firstIndex, secondIndex, suffix] =
969
- /^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\]\[([^\]]+)\](\+\+|--)$/.exec(text);
971
+ /^(@{0,2}\$?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\]\[([^\]]+)\](\+\+|--)$/.exec(text);
970
972
  body.push({
971
973
  type: "indexAssign",
972
974
  receiver,
@@ -974,9 +976,9 @@ class Parser {
974
976
  value: { type: "raw", value: "1" },
975
977
  operator: suffix === "++" ? "+" : "-",
976
978
  });
977
- } else if (/^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\] = (.+)$/.test(text)) {
979
+ } else if (/^(@{0,2}\$?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\] = (.+)$/.test(text)) {
978
980
  const [, receiver, sourceIndex, sourceValue] =
979
- /^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\] = (.+)$/.exec(text);
981
+ /^(@{0,2}\$?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\] = (.+)$/.exec(text);
980
982
  body.push({
981
983
  type: "indexAssign",
982
984
  receiver,
@@ -984,9 +986,9 @@ class Parser {
984
986
  value: this.expression(sourceValue, line),
985
987
  operator: null,
986
988
  });
987
- } else if (/^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\] (\+|-|\*|\/|%|\*\*)= (.+)$/.test(text)) {
989
+ } else if (/^(@{0,2}\$?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\] (\+|-|\*|\/|%|\*\*)= (.+)$/.test(text)) {
988
990
  const [, receiver, sourceIndex, operator, sourceValue] =
989
- /^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\] (\+|-|\*|\/|%|\*\*)= (.+)$/.exec(text);
991
+ /^(@{0,2}\$?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\] (\+|-|\*|\/|%|\*\*)= (.+)$/.exec(text);
990
992
  body.push({
991
993
  type: "indexAssign",
992
994
  receiver,
@@ -994,9 +996,9 @@ class Parser {
994
996
  value: this.expression(sourceValue, line),
995
997
  operator,
996
998
  });
997
- } else if (/^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\](\+\+|--)$/.test(text)) {
999
+ } else if (/^(@{0,2}\$?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\](\+\+|--)$/.test(text)) {
998
1000
  const [, receiver, sourceIndex, suffix] =
999
- /^([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\](\+\+|--)$/.exec(text);
1001
+ /^(@{0,2}\$?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\[([^\]]+)\](\+\+|--)$/.exec(text);
1000
1002
  body.push({
1001
1003
  type: "indexAssign",
1002
1004
  receiver,
@@ -1005,7 +1007,7 @@ class Parser {
1005
1007
  operator: suffix === "++" ? "+" : "-",
1006
1008
  });
1007
1009
  } else {
1008
- const assignment = /^(\$[A-Za-z_]\w*|@{0,2}\$?[A-Za-z_]\w*|[A-Za-z_]\w*(?:\.\$?[A-Za-z_]\w*)+) = (.+)$/.exec(text);
1010
+ const assignment = /^(\$[A-Za-z_]\w*|@{0,2}\$?[A-Za-z_]\w*(?:\.\$?[A-Za-z_]\w*)*) = (.+)$/.exec(text);
1009
1011
  if (!assignment)
1010
1012
  this.fail(line, "LNG1106", `unsupported Faz 2 statement: ${text}`);
1011
1013
  body.push({
@@ -1053,7 +1055,10 @@ class Parser {
1053
1055
  const next = this.peek();
1054
1056
  if (next === undefined || spaces(next) < indent)
1055
1057
  this.fail(line, "LNG1262", "unterminated multi-line expression");
1056
- source += ` ${this.line().text.trim()}`;
1058
+ const continuation = this.line().text.trim();
1059
+ const startsField = /^(?:\$?[A-Za-z_][\w-]*|['"][^'"]+['"]): /.test(continuation);
1060
+ if (startsField && !/[{,]\s*$/.test(source)) source += ",";
1061
+ source += ` ${continuation}`;
1057
1062
  depth = curlyDelimiterDepth(source);
1058
1063
  }
1059
1064
  return source;
@@ -1412,7 +1417,7 @@ function expr(source, line, parser) {
1412
1417
  line: line.number,
1413
1418
  };
1414
1419
  const nestedIndex =
1415
- /^([A-Za-z_]\w*(?:(?:\.|\?\.)[A-Za-z_]\w)*(?:\?\.)?\[.+\])(\?\.)?\[(.+)\]$/.exec(
1420
+ /^(@{0,2}\$?[A-Za-z_]\w*(?:(?:\.|\?\.)[A-Za-z_]\w)*(?:\?\.)?\[.+\])(\?\.)?\[(.+)\]$/.exec(
1416
1421
  text,
1417
1422
  );
1418
1423
  if (nestedIndex)
@@ -1423,7 +1428,7 @@ function expr(source, line, parser) {
1423
1428
  index: expr(nestedIndex[3], line, parser),
1424
1429
  };
1425
1430
  const computedIndex =
1426
- /^([A-Za-z_]\w*(?:(?:\.|\?\.)[A-Za-z_]\w)*)(\?\.)?\[([^\]]+)\]$/.exec(
1431
+ /^(@{0,2}\$?[A-Za-z_]\w*(?:(?:\.|\?\.)[A-Za-z_]\w)*)(\?\.)?\[([^\]]+)\]$/.exec(
1427
1432
  text,
1428
1433
  );
1429
1434
  if (
@@ -1437,7 +1442,7 @@ function expr(source, line, parser) {
1437
1442
  index: expr(computedIndex[3], line, parser),
1438
1443
  };
1439
1444
  const indexedProperty =
1440
- /^([A-Za-z_]\w*.*\])(\?\.|\.)(\$?[A-Za-z_]\w*)$/.exec(text);
1445
+ /^(@{0,2}\$?[A-Za-z_]\w*.*\])(\?\.|\.)(\$?[A-Za-z_]\w*)$/.exec(text);
1441
1446
  if (
1442
1447
  indexedProperty &&
1443
1448
  !/^[A-Za-z_]\w*\?\.\[(?:\d+|[A-Za-z_]\w*|['"][^'"]*['"])\]$/.test(
@@ -1451,7 +1456,7 @@ function expr(source, line, parser) {
1451
1456
  name: indexedProperty[3],
1452
1457
  };
1453
1458
  const indexedMethodCall =
1454
- /^([A-Za-z_]\w*.*\])(\?\.|\.)([A-Za-z_]\w*)\((.*)\)$/.exec(text);
1459
+ /^(@{0,2}\$?[A-Za-z_]\w*.*\])(\?\.|\.)([A-Za-z_]\w*)\((.*)\)$/.exec(text);
1455
1460
  if (indexedMethodCall)
1456
1461
  return {
1457
1462
  type: "methodCall",
@@ -1464,7 +1469,7 @@ function expr(source, line, parser) {
1464
1469
  line: line.number,
1465
1470
  };
1466
1471
  const nestedCallResultIndex =
1467
- /^([A-Za-z_]\w*.*\)\?\.\[.+\])(\?\.)?\[(.+)\]$/.exec(text);
1472
+ /^(@{0,2}\$?[A-Za-z_]\w*.*\)\?\.\[.+\])(\?\.)?\[(.+)\]$/.exec(text);
1468
1473
  if (nestedCallResultIndex)
1469
1474
  return {
1470
1475
  type: "index",
@@ -1473,7 +1478,7 @@ function expr(source, line, parser) {
1473
1478
  index: expr(nestedCallResultIndex[3], line, parser),
1474
1479
  };
1475
1480
  const callResultIndex =
1476
- /^([A-Za-z_]\w*.*\))(\?\.)?\[(.+)\]$/.exec(text);
1481
+ /^(@{0,2}\$?[A-Za-z_]\w*.*\))(\?\.)?\[(.+)\]$/.exec(text);
1477
1482
  if (callResultIndex)
1478
1483
  return {
1479
1484
  type: "index",
@@ -1482,7 +1487,7 @@ function expr(source, line, parser) {
1482
1487
  index: expr(callResultIndex[3], line, parser),
1483
1488
  };
1484
1489
  const callResultProperty =
1485
- /^([A-Za-z_]\w*.*\))(\?\.|\.)(\$?[A-Za-z_]\w*)$/.exec(text);
1490
+ /^(@{0,2}\$?[A-Za-z_]\w*.*\))(\?\.|\.)(\$?[A-Za-z_]\w*)$/.exec(text);
1486
1491
  if (callResultProperty)
1487
1492
  return {
1488
1493
  type: "property",
@@ -1491,7 +1496,7 @@ function expr(source, line, parser) {
1491
1496
  name: callResultProperty[3],
1492
1497
  };
1493
1498
  const callResultMethod =
1494
- /^([A-Za-z_]\w*.*\))(\?\.|\.)([A-Za-z_]\w*)\((.*)\)$/.exec(text);
1499
+ /^(@{0,2}\$?[A-Za-z_]\w*.*\))(\?\.|\.)([A-Za-z_]\w*)\((.*)\)$/.exec(text);
1495
1500
  if (callResultMethod)
1496
1501
  return {
1497
1502
  type: "methodCall",
@@ -1515,7 +1520,7 @@ function expr(source, line, parser) {
1515
1520
  line: line.number,
1516
1521
  };
1517
1522
  const methodCall =
1518
- /^([A-Za-z_]\w*(?:(?:\.|\?\.)[A-Za-z_]\w*)*?)(\?\.|\.)([A-Za-z_]\w*)\((.*)\)$/.exec(
1523
+ /^(@{0,2}\$?[A-Za-z_]\w*(?:(?:\.|\?\.)[A-Za-z_]\w*)*?)(\?\.|\.)([A-Za-z_]\w*)\((.*)\)$/.exec(
1519
1524
  text,
1520
1525
  );
1521
1526
  const namespaceMethodCall =
@@ -1569,15 +1574,15 @@ function expr(source, line, parser) {
1569
1574
  return { type: "raw", value: text };
1570
1575
  }
1571
1576
  if (/^(true|false|null)$/.test(text)) return { type: "raw", value: text };
1572
- if (/^[A-Za-z_]\w*(?:(?:\.|\?\.)[A-Za-z_]\w*)*(?:\.|\?\.)_[A-Za-z_]\w*$/.test(text))
1577
+ if (/^@{0,2}\$?[A-Za-z_]\w*(?:(?:\.|\?\.)[A-Za-z_]\w*)*(?:\.|\?\.)_[A-Za-z_]\w*$/.test(text))
1573
1578
  parser.fail(
1574
1579
  line,
1575
1580
  "LNG1115",
1576
1581
  "protected properties are not accessible outside a class",
1577
1582
  );
1578
- if (/^[A-Za-z_]\w*(?:(?:\.|\?\.)\$?[A-Za-z_]\w*|(?:\?\.)?\[(?:\d+|[A-Za-z_]\w*|['"][^'"]*['"])\])+$/.test(text))
1583
+ if (/^@{0,2}\$?[A-Za-z_]\w*(?:(?:\.|\?\.)\$?[A-Za-z_]\w*|(?:\?\.)?\[(?:\d+|[A-Za-z_]\w*|['"][^'"]*['"])\])+$/.test(text))
1579
1584
  return { type: "raw", value: text };
1580
- if (/^[A-Za-z_]\w*\[(?:\d+|[A-Za-z_]\w*|['"][^'"]*['"])\]$/.test(text))
1585
+ if (/^@{0,2}\$?[A-Za-z_]\w*\[(?:\d+|[A-Za-z_]\w*|['"][^'"]*['"])\]$/.test(text))
1581
1586
  return { type: "raw", value: text };
1582
1587
  if (/^(?:\$[A-Za-z_]\w*|@{0,2}\$?[A-Za-z_]\w*)$/.test(text)) return { type: "raw", value: text };
1583
1588
  parser.fail(line, "LNG1108", `unsupported Faz 2 expression: ${text}`);
@@ -3754,15 +3759,10 @@ function emitStatement(node, depth, declared, flags) {
3754
3759
  }
3755
3760
  if (node.type === "assign") {
3756
3761
  const decimalBinding = node.name.startsWith("$") || /^@{1,2}\$/.test(node.name);
3757
- const property = node.name.slice(node.name.startsWith("@@") ? 2 : 1);
3758
3762
  const target = /^[A-Za-z_]\w*(?:\.\$?[A-Za-z_]\w*)+$/.test(node.name)
3759
- ? node.name
3760
- : node.name.startsWith("@@")
3761
- ? `${flags.className}.${property}`
3763
+ ? emitReceiverName(node.name, flags)
3762
3764
  : node.name.startsWith("@")
3763
- ? flags.publicProperties?.has(property)
3764
- ? `this.${property}`
3765
- : `this.#${property}`
3765
+ ? emitReceiverName(node.name, flags)
3766
3766
  : flags.className && node.name.startsWith("_")
3767
3767
  ? `this.${node.name}`
3768
3768
  : node.name;
@@ -3789,7 +3789,7 @@ function emitStatement(node, depth, declared, flags) {
3789
3789
  return `${pad}${prefix}${target} = ${decimalBinding && node.value.type !== "binary" && !decimalLiteral ? `__decimal(${decimalInput})` : value};\n`;
3790
3790
  }
3791
3791
  if (node.type === "indexAssign") {
3792
- const target = `${node.receiver}${(node.indexes ?? [node.index]).map((index) => `[${emitExpr(index, flags)}]`).join("")}`;
3792
+ const target = `${emitReceiverName(node.receiver, flags)}${(node.indexes ?? [node.index]).map((index) => `[${emitExpr(index, flags)}]`).join("")}`;
3793
3793
  const value = emitExpr(node.value, flags);
3794
3794
  if (node.operator) flags.finite = true;
3795
3795
  return `${pad}${target} = ${node.operator ? `__finite(${target} ${node.operator} ${value})` : value};\n`;
@@ -3967,6 +3967,18 @@ function emitBoundaryChecks(parameters, depth, flags) {
3967
3967
  .join("");
3968
3968
  }
3969
3969
  function emitReceiverName(name, flags) {
3970
+ const member = /^(@{1,2})(\$?[A-Za-z_]\w*)(.*)$/.exec(name);
3971
+ if (member) {
3972
+ const [, scope, property, suffix] = member;
3973
+ const owner = scope === "@@" ? flags.className : "this";
3974
+ const visibility = scope === "@@" || flags.publicProperties?.has(property) ? "" : "#";
3975
+ return `${owner}.${visibility}${property}${suffix}`;
3976
+ }
3977
+ const base = /^([A-Za-z_]\w*)([.\[].*)$/.exec(name);
3978
+ if (base && flags.contextNames?.has(base[1])) {
3979
+ flags.context = true;
3980
+ return `__contextGet(${JSON.stringify(base[1])})${base[2]}`;
3981
+ }
3970
3982
  if (!flags.contextNames?.has(name)) return name;
3971
3983
  flags.context = true;
3972
3984
  return `__contextGet(${JSON.stringify(name)})`;
@@ -3985,11 +3997,18 @@ function emitExpr(node, flags) {
3985
3997
  flags.normalize = true;
3986
3998
  return `__normalize(${value})`;
3987
3999
  };
4000
+ if (node.type === "raw")
4001
+ if (/^@{1,2}\$?[A-Za-z_]\w*[.\[?]/.test(node.value))
4002
+ return normalize(emitReceiverName(node.value, flags));
3988
4003
  if (node.type === "raw")
3989
4004
  if (flags.contextNames?.has(node.value)) {
3990
4005
  flags.context = true;
3991
4006
  return `__contextGet(${JSON.stringify(node.value)})`;
3992
4007
  }
4008
+ if (node.type === "raw")
4009
+ if (/^[A-Za-z_]\w*[.\[]/.test(node.value) && flags.contextNames?.has(node.value.split(/[.\[]/)[0])) {
4010
+ return normalize(emitReceiverName(node.value, flags));
4011
+ }
3993
4012
  if (node.type === "raw")
3994
4013
  if (/^DB\.(Active|RollbackOnly|Failed)$/.test(node.value)) {
3995
4014
  flags.databaseEnum = true;