@lokascript/framework 2.1.0 → 2.2.0
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/dist/api/index.js +111 -24
- package/dist/api/index.js.map +1 -1
- package/dist/core/index.js +133 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/pattern-matching/index.js.map +1 -1
- package/dist/core/tokenization/index.js +133 -0
- package/dist/core/tokenization/index.js.map +1 -1
- package/dist/core/tokenization/morphology/base-normalizer.d.ts +94 -0
- package/dist/core/tokenization/morphology/base-normalizer.d.ts.map +1 -0
- package/dist/core/tokenization/morphology/index.d.ts +3 -1
- package/dist/core/tokenization/morphology/index.d.ts.map +1 -1
- package/dist/core/types.d.ts +3 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/generation/index.js.map +1 -1
- package/dist/index.cjs +564 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +556 -46
- package/dist/index.js.map +1 -1
- package/dist/ir/explicit-parser.d.ts +62 -7
- package/dist/ir/explicit-parser.d.ts.map +1 -1
- package/dist/ir/explicit-renderer.d.ts +18 -1
- package/dist/ir/explicit-renderer.d.ts.map +1 -1
- package/dist/ir/index.d.ts +4 -2
- package/dist/ir/index.d.ts.map +1 -1
- package/dist/ir/index.js +423 -46
- package/dist/ir/index.js.map +1 -1
- package/dist/ir/protocol-json.d.ts.map +1 -1
- package/dist/ir/to-runtime-ast.d.ts +53 -0
- package/dist/ir/to-runtime-ast.d.ts.map +1 -0
- package/dist/ir/types.d.ts +14 -4
- package/dist/ir/types.d.ts.map +1 -1
- package/dist/parsing/index.js.map +1 -1
- package/dist/testing/index.js +8671 -8261
- package/dist/testing/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/tokenization/morphology/base-normalizer.ts +249 -0
- package/src/core/tokenization/morphology/index.ts +3 -1
- package/src/core/types.ts +4 -2
- package/src/index.ts +7 -0
- package/src/ir/conformance.test.ts +120 -0
- package/src/ir/explicit-parser.test.ts +307 -1
- package/src/ir/explicit-parser.ts +403 -29
- package/src/ir/explicit-renderer.test.ts +87 -2
- package/src/ir/explicit-renderer.ts +57 -5
- package/src/ir/index.ts +13 -2
- package/src/ir/protocol-json.test.ts +4 -2
- package/src/ir/protocol-json.ts +30 -24
- package/src/ir/to-runtime-ast.ts +203 -0
- package/src/ir/types.ts +14 -4
package/dist/api/index.js
CHANGED
|
@@ -1696,6 +1696,8 @@ function isNestedCommand(value) {
|
|
|
1696
1696
|
}
|
|
1697
1697
|
function parseExplicit(input, options = {}) {
|
|
1698
1698
|
const trimmed = input.trim();
|
|
1699
|
+
const collect = options.collectDiagnostics === true;
|
|
1700
|
+
const diagnostics = [];
|
|
1699
1701
|
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
|
|
1700
1702
|
throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
|
|
1701
1703
|
}
|
|
@@ -1718,10 +1720,30 @@ function parseExplicit(input, options = {}) {
|
|
|
1718
1720
|
const enabled = token.startsWith("+");
|
|
1719
1721
|
const flagName = token.slice(1);
|
|
1720
1722
|
if (!flagName) {
|
|
1723
|
+
if (collect) {
|
|
1724
|
+
diagnostics.push({
|
|
1725
|
+
severity: "error",
|
|
1726
|
+
code: "EMPTY_FLAG",
|
|
1727
|
+
message: `Empty flag name: "${token}"`,
|
|
1728
|
+
source: "schema"
|
|
1729
|
+
});
|
|
1730
|
+
continue;
|
|
1731
|
+
}
|
|
1721
1732
|
throw new Error(`Empty flag name: "${token}"`);
|
|
1722
1733
|
}
|
|
1723
1734
|
if (validRoleNames && !validRoleNames.has(flagName)) {
|
|
1724
1735
|
const roleList = [...validRoleNames].join(", ");
|
|
1736
|
+
if (collect) {
|
|
1737
|
+
diagnostics.push({
|
|
1738
|
+
severity: "error",
|
|
1739
|
+
code: "UNKNOWN_FLAG",
|
|
1740
|
+
message: `Unknown flag "${flagName}" for command "${command}"`,
|
|
1741
|
+
source: "schema",
|
|
1742
|
+
suggestions: [...validRoleNames].map((r) => `+${r}`)
|
|
1743
|
+
});
|
|
1744
|
+
roles.set(flagName, createFlag(flagName, enabled));
|
|
1745
|
+
continue;
|
|
1746
|
+
}
|
|
1725
1747
|
throw new Error(
|
|
1726
1748
|
`Unknown flag "${flagName}" for command "${command}". Valid roles: ${roleList}`
|
|
1727
1749
|
);
|
|
@@ -1731,14 +1753,33 @@ function parseExplicit(input, options = {}) {
|
|
|
1731
1753
|
}
|
|
1732
1754
|
const colonIndex = token.indexOf(":");
|
|
1733
1755
|
if (colonIndex === -1) {
|
|
1756
|
+
if (collect) {
|
|
1757
|
+
diagnostics.push({
|
|
1758
|
+
severity: "error",
|
|
1759
|
+
code: "INVALID_ROLE_FORMAT",
|
|
1760
|
+
message: `Invalid role format: "${token}". Expected role:value or +flag`,
|
|
1761
|
+
source: "schema"
|
|
1762
|
+
});
|
|
1763
|
+
continue;
|
|
1764
|
+
}
|
|
1734
1765
|
throw new Error(`Invalid role format: "${token}". Expected role:value or +flag`);
|
|
1735
1766
|
}
|
|
1736
1767
|
const roleName = token.slice(0, colonIndex);
|
|
1737
1768
|
if (validRoleNames && !STRUCTURAL_ROLES.has(roleName) && !validRoleNames.has(roleName)) {
|
|
1738
1769
|
const roleList = [...validRoleNames].join(", ");
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1770
|
+
if (collect) {
|
|
1771
|
+
diagnostics.push({
|
|
1772
|
+
severity: "error",
|
|
1773
|
+
code: "UNKNOWN_ROLE",
|
|
1774
|
+
message: `Unknown role "${roleName}" for command "${command}"`,
|
|
1775
|
+
source: "schema",
|
|
1776
|
+
suggestions: [...validRoleNames]
|
|
1777
|
+
});
|
|
1778
|
+
} else {
|
|
1779
|
+
throw new Error(
|
|
1780
|
+
`Unknown role "${roleName}" for command "${command}". Valid roles: ${roleList}`
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1742
1783
|
}
|
|
1743
1784
|
const role = roleName;
|
|
1744
1785
|
const valueStr = token.slice(colonIndex + 1);
|
|
@@ -1754,37 +1795,67 @@ function parseExplicit(input, options = {}) {
|
|
|
1754
1795
|
if (schema && command !== "on") {
|
|
1755
1796
|
for (const roleSpec of schema.roles) {
|
|
1756
1797
|
if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1798
|
+
if (collect) {
|
|
1799
|
+
diagnostics.push({
|
|
1800
|
+
severity: "error",
|
|
1801
|
+
code: "MISSING_REQUIRED_ROLE",
|
|
1802
|
+
message: `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`,
|
|
1803
|
+
source: "schema"
|
|
1804
|
+
});
|
|
1805
|
+
} else {
|
|
1806
|
+
throw new Error(
|
|
1807
|
+
`Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`
|
|
1808
|
+
);
|
|
1809
|
+
}
|
|
1760
1810
|
}
|
|
1761
1811
|
}
|
|
1762
1812
|
}
|
|
1813
|
+
const attachDiagnostics = (node) => {
|
|
1814
|
+
if (diagnostics.length === 0) return node;
|
|
1815
|
+
return { ...node, diagnostics };
|
|
1816
|
+
};
|
|
1763
1817
|
switch (command) {
|
|
1764
1818
|
case "on": {
|
|
1765
1819
|
const eventValue = roles.get("event");
|
|
1766
1820
|
if (!eventValue) {
|
|
1821
|
+
if (collect) {
|
|
1822
|
+
diagnostics.push({
|
|
1823
|
+
severity: "error",
|
|
1824
|
+
code: "MISSING_EVENT_ROLE",
|
|
1825
|
+
message: "Event handler requires event role: [on event:click ...]",
|
|
1826
|
+
source: "schema"
|
|
1827
|
+
});
|
|
1828
|
+
return attachDiagnostics(
|
|
1829
|
+
createEventHandlerNode("on", roles, [], {
|
|
1830
|
+
sourceLanguage: "explicit"
|
|
1831
|
+
})
|
|
1832
|
+
);
|
|
1833
|
+
}
|
|
1767
1834
|
throw new Error("Event handler requires event role: [on event:click ...]");
|
|
1768
1835
|
}
|
|
1769
1836
|
const body = extractStructuralBody(roles, "body", options);
|
|
1770
1837
|
roles.delete("body");
|
|
1771
|
-
return
|
|
1772
|
-
|
|
1773
|
-
|
|
1838
|
+
return attachDiagnostics(
|
|
1839
|
+
createEventHandlerNode("on", roles, body, {
|
|
1840
|
+
sourceLanguage: "explicit"
|
|
1841
|
+
})
|
|
1842
|
+
);
|
|
1774
1843
|
}
|
|
1775
1844
|
case "if": {
|
|
1776
1845
|
const thenBranch = extractStructuralBody(roles, "then", options);
|
|
1777
1846
|
const elseBranch = extractStructuralBody(roles, "else", options);
|
|
1778
1847
|
roles.delete("then");
|
|
1779
1848
|
roles.delete("else");
|
|
1780
|
-
return
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1849
|
+
return attachDiagnostics(
|
|
1850
|
+
createConditionalNode(
|
|
1851
|
+
command,
|
|
1852
|
+
roles,
|
|
1853
|
+
thenBranch,
|
|
1854
|
+
elseBranch.length > 0 ? elseBranch : void 0,
|
|
1855
|
+
{
|
|
1856
|
+
sourceLanguage: "explicit"
|
|
1857
|
+
}
|
|
1858
|
+
)
|
|
1788
1859
|
);
|
|
1789
1860
|
}
|
|
1790
1861
|
case "repeat": {
|
|
@@ -1810,14 +1881,18 @@ function parseExplicit(input, options = {}) {
|
|
|
1810
1881
|
}
|
|
1811
1882
|
const loopVariable = variableValue?.type === "literal" && typeof variableValue.value === "string" ? variableValue.value : variableValue?.type === "expression" ? variableValue.raw : void 0;
|
|
1812
1883
|
const indexVariable = indexVariableValue?.type === "literal" && typeof indexVariableValue.value === "string" ? indexVariableValue.value : void 0;
|
|
1813
|
-
return
|
|
1814
|
-
|
|
1815
|
-
|
|
1884
|
+
return attachDiagnostics(
|
|
1885
|
+
createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {
|
|
1886
|
+
sourceLanguage: "explicit"
|
|
1887
|
+
})
|
|
1888
|
+
);
|
|
1816
1889
|
}
|
|
1817
1890
|
default:
|
|
1818
|
-
return
|
|
1819
|
-
|
|
1820
|
-
|
|
1891
|
+
return attachDiagnostics(
|
|
1892
|
+
createCommandNode(command, roles, {
|
|
1893
|
+
sourceLanguage: "explicit"
|
|
1894
|
+
})
|
|
1895
|
+
);
|
|
1821
1896
|
}
|
|
1822
1897
|
}
|
|
1823
1898
|
function isExplicitSyntax(input) {
|
|
@@ -1925,11 +2000,23 @@ function findMatchingBracket(str, start) {
|
|
|
1925
2000
|
|
|
1926
2001
|
// src/ir/explicit-renderer.ts
|
|
1927
2002
|
function renderExplicit(node) {
|
|
2003
|
+
let result;
|
|
1928
2004
|
if (node.kind === "compound") {
|
|
1929
2005
|
const compoundNode = node;
|
|
1930
2006
|
const renderedStatements = compoundNode.statements.map((stmt) => renderExplicit(stmt));
|
|
1931
|
-
|
|
2007
|
+
result = renderedStatements.join(` ${compoundNode.chainType} `);
|
|
2008
|
+
} else {
|
|
2009
|
+
result = renderBracketCommand(node);
|
|
2010
|
+
}
|
|
2011
|
+
if (node.annotations && node.annotations.length > 0) {
|
|
2012
|
+
const annParts = node.annotations.map(
|
|
2013
|
+
(ann) => ann.value !== void 0 ? `@${ann.name}(${ann.value})` : `@${ann.name}`
|
|
2014
|
+
);
|
|
2015
|
+
return annParts.join(" ") + " " + result;
|
|
1932
2016
|
}
|
|
2017
|
+
return result;
|
|
2018
|
+
}
|
|
2019
|
+
function renderBracketCommand(node) {
|
|
1933
2020
|
const parts = [node.action];
|
|
1934
2021
|
for (const [role, value] of node.roles) {
|
|
1935
2022
|
if (value.type === "flag") {
|