iobroker.statechart 0.2.0 → 0.3.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.
@@ -31,7 +31,7 @@ __export(chart_validation_exports, {
31
31
  GLOBALS: () => GLOBALS,
32
32
  getDeclarations: () => getDeclarations,
33
33
  getFunctionNames: () => getFunctionNames,
34
- quoteChangeArguments: () => quoteChangeArguments,
34
+ prepareHelperCalls: () => prepareHelperCalls,
35
35
  validateChart: () => validateChart,
36
36
  validateCode: () => validateCode,
37
37
  validateTransitionLabel: () => validateTransitionLabel
@@ -39,11 +39,15 @@ __export(chart_validation_exports, {
39
39
  module.exports = __toCommonJS(chart_validation_exports);
40
40
  var acorn = __toESM(require("acorn"));
41
41
  var walk = __toESM(require("acorn-walk"));
42
+ var import_chart_analysis = require("./chart-analysis");
42
43
  var import_chart_helpers = require("./chart-helpers");
43
44
  var import_chart_model = require("./chart-model");
45
+ var import_chart_structure = require("./chart-structure");
44
46
  const GLOBALS = /* @__PURE__ */ new Set([
45
47
  // the helper functions, e.g. log() and setState()
46
48
  ...Object.keys(import_chart_helpers.HELPERS),
49
+ // the time units, e.g. elapsed(min)
50
+ ...Object.keys(import_chart_model.TIME_UNITS),
47
51
  "undefined",
48
52
  "NaN",
49
53
  "Infinity",
@@ -123,7 +127,25 @@ function validateCode(code, kind, symbols, offset = 0, functionNames = []) {
123
127
  const diagnostics = [];
124
128
  walk.simple(parsed.ast, {
125
129
  CallExpression(node) {
126
- if (node.callee.type !== "Identifier" || !import_chart_helpers.CHANGE_HELPERS.has(node.callee.name)) {
130
+ if (node.callee.type !== "Identifier") {
131
+ return;
132
+ }
133
+ if (node.callee.name === "getState" && !declared.has("getState")) {
134
+ const arg2 = node.arguments[0];
135
+ if (!(arg2?.type === "Literal" && typeof arg2.value === "string" && arg2.value.trim())) {
136
+ const { start, end } = arg2 ?? node;
137
+ diagnostics.push(
138
+ createDiagnostic(
139
+ start + offset,
140
+ end + offset,
141
+ "error",
142
+ 'getState() needs the ID of the state as text, e.g. getState("0_userdata.0.temperature")'
143
+ )
144
+ );
145
+ }
146
+ return;
147
+ }
148
+ if (!import_chart_helpers.CHANGE_HELPERS.has(node.callee.name)) {
127
149
  return;
128
150
  }
129
151
  const arg = node.arguments[0];
@@ -172,7 +194,8 @@ function validateTransitionLabel(label, symbols, functionNames = []) {
172
194
  }
173
195
  const { label: parts, ranges } = parsed;
174
196
  const diagnostics = [];
175
- if (parts.event && ranges.event && !symbols.some((s) => s.scope === "input" && s.name === parts.event)) {
197
+ const isInput = (name) => symbols.some((s) => s.scope === "input" && s.name === name);
198
+ if (parts.event && ranges.event && !isInput(parts.event)) {
176
199
  diagnostics.push(
177
200
  createDiagnostic(
178
201
  ranges.event.from,
@@ -183,6 +206,23 @@ function validateTransitionLabel(label, symbols, functionNames = []) {
183
206
  )
184
207
  );
185
208
  }
209
+ const temporal = parts.temporal;
210
+ if (temporal?.event && ranges.temporalEvent && !isInput(temporal.event)) {
211
+ diagnostics.push(
212
+ createDiagnostic(
213
+ ranges.temporalEvent.from,
214
+ ranges.temporalEvent.to,
215
+ "error",
216
+ 'Unknown input "%s", use a time unit (msec, sec, min) or the name of an input',
217
+ [temporal.event]
218
+ )
219
+ );
220
+ }
221
+ if (temporal?.condition && ranges.temporalCondition) {
222
+ diagnostics.push(
223
+ ...validateCode(temporal.condition, "expression", symbols, ranges.temporalCondition.from, functionNames)
224
+ );
225
+ }
186
226
  if (parts.condition && ranges.condition) {
187
227
  diagnostics.push(...validateCode(parts.condition, "expression", symbols, ranges.condition.from, functionNames));
188
228
  }
@@ -222,34 +262,45 @@ function getDeclarations(code) {
222
262
  function getFunctionNames(chart) {
223
263
  return (chart.functions || []).flatMap((fn) => getDeclarations(fn.code).map((d) => d.name));
224
264
  }
225
- function quoteChangeArguments(code, kind) {
265
+ function prepareHelperCalls(code, kind, createCallId) {
226
266
  const parsed = code.trim() ? parseCode(code, kind) : null;
227
267
  if (!parsed || "diagnostic" in parsed) {
228
- return { code, names: [] };
268
+ return { code, names: [], stateIds: [] };
229
269
  }
230
270
  const names = /* @__PURE__ */ new Set();
271
+ const stateIds = /* @__PURE__ */ new Set();
231
272
  const replacements = [];
232
273
  walk.simple(parsed.ast, {
233
274
  CallExpression(node) {
234
- const arg = node.arguments[0];
235
- if (node.callee.type !== "Identifier" || !import_chart_helpers.CHANGE_HELPERS.has(node.callee.name)) {
275
+ if (node.callee.type !== "Identifier") {
236
276
  return;
237
277
  }
238
- if (arg?.type === "Identifier") {
239
- replacements.push(arg);
240
- names.add(arg.name);
241
- } else if (arg?.type === "Literal" && typeof arg.value === "string") {
242
- names.add(arg.value);
278
+ const arg = node.arguments[0];
279
+ if (node.callee.name === "getState") {
280
+ if (arg?.type === "Literal" && typeof arg.value === "string") {
281
+ stateIds.add(arg.value);
282
+ }
283
+ } else if (node.callee.name === "duration") {
284
+ const at = code.indexOf("(", node.callee.end) + 1;
285
+ const id = JSON.stringify(createCallId());
286
+ replacements.push({ start: at, end: at, text: node.arguments.length ? `${id}, ` : id });
287
+ } else if (import_chart_helpers.CHANGE_HELPERS.has(node.callee.name)) {
288
+ if (arg?.type === "Identifier") {
289
+ replacements.push({ start: arg.start, end: arg.end, text: JSON.stringify(arg.name) });
290
+ names.add(arg.name);
291
+ } else if (arg?.type === "Literal" && typeof arg.value === "string") {
292
+ names.add(arg.value);
293
+ }
243
294
  }
244
295
  }
245
296
  });
246
297
  let result = code;
247
- for (const { start, end, name } of replacements.sort((a, b) => b.start - a.start)) {
248
- result = result.substring(0, start) + JSON.stringify(name) + result.substring(end);
298
+ for (const { start, end, text } of replacements.sort((a, b) => b.start - a.start)) {
299
+ result = result.substring(0, start) + text + result.substring(end);
249
300
  }
250
- return { code: result, names: [...names] };
301
+ return { code: result, names: [...names], stateIds: [...stateIds] };
251
302
  }
252
- function validateChart(chart) {
303
+ function validateChart(chart, options = {}) {
253
304
  const functionNames = getFunctionNames(chart);
254
305
  const problems = [];
255
306
  for (const state of chart.states) {
@@ -259,10 +310,20 @@ function validateChart(chart) {
259
310
  }
260
311
  }
261
312
  }
313
+ const structure = new import_chart_structure.ChartStructure(chart);
262
314
  for (const transition of chart.transitions) {
263
315
  for (const diagnostic of validateTransitionLabel(transition.label, chart.symbols, functionNames)) {
264
316
  problems.push({ kind: "transition", id: transition.id, field: "label", diagnostic });
265
317
  }
318
+ if (structure.getCrossedParallel(transition) !== void 0) {
319
+ problems.push({
320
+ kind: "transition",
321
+ id: transition.id,
322
+ field: "label",
323
+ // parallel states are active at the same time, there is nothing to switch between
324
+ diagnostic: createDiagnostic(0, 0, "error", "Transitions between parallel states are not possible")
325
+ });
326
+ }
266
327
  }
267
328
  const symbolNames = new Set(chart.symbols.map((s) => s.name));
268
329
  const seen = /* @__PURE__ */ new Set();
@@ -288,6 +349,14 @@ function validateChart(chart) {
288
349
  seen.add(name);
289
350
  }
290
351
  }
352
+ for (const { kind, id, severity, key, args } of options.analyze === false ? [] : (0, import_chart_analysis.analyzeChart)(chart)) {
353
+ problems.push({
354
+ kind,
355
+ id,
356
+ field: kind === "transition" ? "label" : "",
357
+ diagnostic: createDiagnostic(0, 0, severity, key, args)
358
+ });
359
+ }
291
360
  return problems;
292
361
  }
293
362
  // Annotate the CommonJS export names for ESM import in node:
@@ -295,7 +364,7 @@ function validateChart(chart) {
295
364
  GLOBALS,
296
365
  getDeclarations,
297
366
  getFunctionNames,
298
- quoteChangeArguments,
367
+ prepareHelperCalls,
299
368
  validateChart,
300
369
  validateCode,
301
370
  validateTransitionLabel
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/chart-validation.ts"],
4
- "sourcesContent": ["/*\n * Checks conditions and actions of a chart: JavaScript syntax, unknown variables and events.\n * Used by the editor (to underline errors) and by the runtime (to log them when a chart starts).\n */\n\nimport * as acorn from \"acorn\";\nimport * as walk from \"acorn-walk\";\nimport { CHANGE_HELPERS, HELPERS } from \"./chart-helpers\";\nimport { LabelSyntaxError, formatMessage, parseTransitionLabelWithRanges } from \"./chart-model\";\nimport type { Chart, ChartSymbol } from \"./chart-model\";\n\n/** Names which can be used in conditions and actions besides the chart variables */\nexport const GLOBALS = new Set([\n // the helper functions, e.g. log() and setState()\n ...Object.keys(HELPERS),\n \"undefined\",\n \"NaN\",\n \"Infinity\",\n \"Math\",\n \"Date\",\n \"JSON\",\n \"Number\",\n \"String\",\n \"Boolean\",\n \"Array\",\n \"Object\",\n \"RegExp\",\n \"Map\",\n \"Set\",\n \"Error\",\n \"parseInt\",\n \"parseFloat\",\n \"isNaN\",\n \"isFinite\",\n \"encodeURIComponent\",\n \"decodeURIComponent\",\n]);\n\n/** Fields of a state which contain actions */\nconst STATE_ACTIONS = [\"entry\", \"during\", \"exit\"] as const;\n\nexport type CodeKind = \"expression\" | \"statements\";\n\nexport interface Diagnostic {\n /** Position in the checked text */\n from: number;\n to: number;\n severity: \"error\" | \"warning\";\n /** English message */\n message: string;\n /** Message with %s placeholders, used to translate it */\n key: string;\n args: string[];\n}\n\nfunction createDiagnostic(\n from: number,\n to: number,\n severity: \"error\" | \"warning\",\n key: string,\n args: string[] = [],\n): Diagnostic {\n return { from, to, severity, key, args, message: formatMessage(key, args) };\n}\n\n/**\n * Parses JavaScript and converts syntax errors to diagnostics\n *\n * @param code The code to parse\n * @param kind Expression or statements\n */\nfunction parseCode(code: string, kind: CodeKind): { ast: acorn.Node } | { diagnostic: Diagnostic } {\n try {\n if (kind === \"statements\") {\n return { ast: acorn.parse(code, { ecmaVersion: \"latest\", sourceType: \"script\" }) };\n }\n const ast = acorn.parseExpressionAt(code, 0, { ecmaVersion: \"latest\" });\n const rest = code.substring(ast.end);\n if (rest.trim()) {\n const from = ast.end + rest.length - rest.trimStart().length;\n return { diagnostic: createDiagnostic(from, code.length, \"error\", 'Unexpected \"%s\"', [rest.trim()]) };\n }\n return { ast };\n } catch (err) {\n const pos = typeof err.pos === \"number\" ? Math.min(err.pos, code.length) : 0;\n // acorn adds the position \"(line:column)\" to its messages, it is shown by the underline anyway\n const message = String(err.message).replace(/\\s*\\(\\d+:\\d+\\)$/, \"\");\n // underline the token at the error position, or the last character if the code ends unexpectedly\n const from = pos < code.length ? pos : Math.max(0, code.length - 1);\n const token = /^(\\w+|\\S)/.exec(code.substring(from));\n const to = Math.min(code.length, from + (token ? token[0].length : 1));\n return { diagnostic: createDiagnostic(from, to, \"error\", \"Syntax error: %s\", [message]) };\n }\n}\n\n/**\n * Checks a condition or action\n *\n * @param code The code to check\n * @param kind Condition (expression) or action (statements)\n * @param symbols The variables of the chart\n * @param offset Position of the code in the text, added to all positions\n * @param functionNames Names declared in the function blocks of the chart\n */\nexport function validateCode(\n code: string | undefined,\n kind: CodeKind,\n symbols: ChartSymbol[],\n offset = 0,\n functionNames: string[] = [],\n): Diagnostic[] {\n if (!code?.trim()) {\n return [];\n }\n const parsed = parseCode(code, kind);\n if (\"diagnostic\" in parsed) {\n const d = parsed.diagnostic;\n return [{ ...d, from: d.from + offset, to: d.to + offset }];\n }\n\n /** names declared in the code itself (let, const, function parameters, ...) */\n const declared = new Set<string>();\n const references: { node: acorn.Identifier; write: boolean }[] = [];\n walk.fullAncestor(parsed.ast, (node, _state, ancestors, type) => {\n if (node.type !== \"Identifier\") {\n return;\n }\n const parent = ancestors[ancestors.length - 2] as acorn.AnyNode | undefined;\n if (type === \"VariablePattern\") {\n // acorn-walk uses \"VariablePattern\" for declarations and for assignment targets\n if (parent?.type === \"AssignmentExpression\" && parent.left === node) {\n references.push({ node, write: true });\n } else {\n declared.add(node.name);\n }\n } else {\n references.push({ node, write: parent?.type === \"UpdateExpression\" });\n }\n });\n\n const inputs = new Set(symbols.filter(s => s.scope === \"input\").map(s => s.name));\n const symbolNames = new Set(symbols.map(s => s.name));\n const known = new Set([...symbolNames, ...functionNames]);\n const diagnostics: Diagnostic[] = [];\n\n // the change detection needs a variable of the chart, not any value\n walk.simple(parsed.ast, {\n CallExpression(node) {\n if (node.callee.type !== \"Identifier\" || !CHANGE_HELPERS.has(node.callee.name)) {\n return;\n }\n const arg = node.arguments[0];\n let valid;\n if (arg?.type === \"Identifier\") {\n // an unknown name is already reported as unknown variable\n valid =\n symbolNames.has(arg.name) ||\n !(known.has(arg.name) || declared.has(arg.name) || GLOBALS.has(arg.name));\n } else {\n valid = arg?.type === \"Literal\" && typeof arg.value === \"string\" && symbolNames.has(arg.value);\n }\n if (!valid) {\n const { start, end } = arg ?? node;\n diagnostics.push(\n createDiagnostic(\n start + offset,\n end + offset,\n \"error\",\n \"%s() needs a variable of the chart as first argument\",\n [node.callee.name],\n ),\n );\n }\n },\n });\n for (const { node, write } of references) {\n const from = node.start + offset;\n const to = node.end + offset;\n if (!known.has(node.name) && !declared.has(node.name) && !GLOBALS.has(node.name)) {\n diagnostics.push(createDiagnostic(from, to, \"error\", 'Unknown variable \"%s\"', [node.name]));\n } else if (write && inputs.has(node.name) && !declared.has(node.name)) {\n diagnostics.push(\n createDiagnostic(from, to, \"warning\", 'Input \"%s\" is overwritten on its next change', [node.name]),\n );\n }\n }\n return diagnostics;\n}\n\n/**\n * Checks a transition label: its structure, the event and the JavaScript of condition and action\n *\n * @param label The label text\n * @param symbols The variables of the chart\n * @param functionNames Names declared in the function blocks of the chart\n */\nexport function validateTransitionLabel(\n label: string | undefined,\n symbols: ChartSymbol[],\n functionNames: string[] = [],\n): Diagnostic[] {\n let parsed;\n try {\n parsed = parseTransitionLabelWithRanges(label);\n } catch (err) {\n if (err instanceof LabelSyntaxError) {\n return [createDiagnostic(err.from, err.to, \"error\", err.key, err.args)];\n }\n throw err;\n }\n const { label: parts, ranges } = parsed;\n\n const diagnostics: Diagnostic[] = [];\n if (parts.event && ranges.event && !symbols.some(s => s.scope === \"input\" && s.name === parts.event)) {\n diagnostics.push(\n createDiagnostic(\n ranges.event.from,\n ranges.event.to,\n \"error\",\n 'Unknown event \"%s\", use the name of an input',\n [parts.event],\n ),\n );\n }\n if (parts.condition && ranges.condition) {\n diagnostics.push(...validateCode(parts.condition, \"expression\", symbols, ranges.condition.from, functionNames));\n }\n if (parts.action && ranges.action) {\n diagnostics.push(...validateCode(parts.action, \"statements\", symbols, ranges.action.from, functionNames));\n }\n return diagnostics;\n}\n\n/** A name declared at the top level of a function block */\nexport interface Declaration {\n name: string;\n /** e.g. \"clamp(x, lo, hi)\", null if it is no function */\n signature: string | null;\n /** Position of the name in the code */\n from: number;\n to: number;\n}\n\n/**\n * Finds the names declared at the top level of a function block: functions, but also constants etc.\n *\n * @param code The code of the function block\n * @returns empty if the code has syntax errors\n */\nexport function getDeclarations(code: string | undefined): Declaration[] {\n if (!code?.trim()) {\n return [];\n }\n const parsed = parseCode(code, \"statements\");\n if (\"diagnostic\" in parsed) {\n return [];\n }\n const signature = (fn: acorn.Function, name: string): string =>\n `${name}(${fn.params.map(p => code.substring(p.start, p.end)).join(\", \")})`;\n\n const declarations: Declaration[] = [];\n for (const node of (parsed.ast as acorn.Program).body) {\n if (node.type === \"FunctionDeclaration\" && node.id) {\n const { name, start, end } = node.id;\n declarations.push({ name, signature: signature(node, name), from: start, to: end });\n } else if (node.type === \"VariableDeclaration\") {\n for (const declarator of node.declarations) {\n if (declarator.id.type !== \"Identifier\") {\n continue;\n }\n const { name, start, end } = declarator.id;\n const init = declarator.init;\n const isFunction = init?.type === \"ArrowFunctionExpression\" || init?.type === \"FunctionExpression\";\n declarations.push({ name, signature: isFunction ? signature(init, name) : null, from: start, to: end });\n }\n }\n }\n return declarations;\n}\n\n/**\n * Names declared in all function blocks of a chart\n *\n * @param chart The chart\n */\nexport function getFunctionNames(chart: Chart): string[] {\n return (chart.functions || []).flatMap(fn => getDeclarations(fn.code).map(d => d.name));\n}\n\n/**\n * Replaces the variable in the first argument of hasChanged(), hasChangedFrom() and hasChangedTo() by its name\n * in quotes: these helpers need the name of the variable, not its value\n *\n * @param code The code\n * @param kind Expression or statements\n * @returns the changed code and the names of the variables, the unchanged code if it has syntax errors\n */\nexport function quoteChangeArguments(code: string, kind: CodeKind): { code: string; names: string[] } {\n const parsed = code.trim() ? parseCode(code, kind) : null;\n if (!parsed || \"diagnostic\" in parsed) {\n return { code, names: [] };\n }\n const names = new Set<string>();\n const replacements: acorn.Identifier[] = [];\n walk.simple(parsed.ast, {\n CallExpression(node) {\n const arg = node.arguments[0];\n if (node.callee.type !== \"Identifier\" || !CHANGE_HELPERS.has(node.callee.name)) {\n return;\n }\n if (arg?.type === \"Identifier\") {\n replacements.push(arg);\n names.add(arg.name);\n } else if (arg?.type === \"Literal\" && typeof arg.value === \"string\") {\n names.add(arg.value);\n }\n },\n });\n let result = code;\n for (const { start, end, name } of replacements.sort((a, b) => b.start - a.start)) {\n result = result.substring(0, start) + JSON.stringify(name) + result.substring(end);\n }\n return { code: result, names: [...names] };\n}\n\nexport interface ChartProblem {\n kind: \"state\" | \"transition\" | \"function\";\n /** ID of the state, transition or function block */\n id: string;\n /** \"entry\", \"during\", \"exit\", \"label\" or \"code\" */\n field: string;\n diagnostic: Diagnostic;\n}\n\n/**\n * Checks all actions, transition labels and function blocks of a chart\n *\n * @param chart The chart\n */\nexport function validateChart(chart: Chart): ChartProblem[] {\n const functionNames = getFunctionNames(chart);\n const problems: ChartProblem[] = [];\n for (const state of chart.states) {\n for (const field of STATE_ACTIONS) {\n for (const diagnostic of validateCode(state[field], \"statements\", chart.symbols, 0, functionNames)) {\n problems.push({ kind: \"state\", id: state.id, field, diagnostic });\n }\n }\n }\n for (const transition of chart.transitions) {\n for (const diagnostic of validateTransitionLabel(transition.label, chart.symbols, functionNames)) {\n problems.push({ kind: \"transition\", id: transition.id, field: \"label\", diagnostic });\n }\n }\n\n // a name may only be declared once in all function blocks, and not hide a variable\n const symbolNames = new Set(chart.symbols.map(s => s.name));\n const seen = new Set<string>();\n for (const fn of chart.functions || []) {\n for (const diagnostic of validateCode(fn.code, \"statements\", chart.symbols, 0, functionNames)) {\n problems.push({ kind: \"function\", id: fn.id, field: \"code\", diagnostic });\n }\n for (const { name, from, to } of getDeclarations(fn.code)) {\n let key = null;\n if (seen.has(name)) {\n key = '\"%s\" is declared in more than one function block';\n } else if (symbolNames.has(name) || GLOBALS.has(name)) {\n key = '\"%s\" is already the name of a variable';\n }\n if (key) {\n problems.push({\n kind: \"function\",\n id: fn.id,\n field: \"code\",\n diagnostic: createDiagnostic(from, to, \"error\", key, [name]),\n });\n }\n seen.add(name);\n }\n }\n return problems;\n}\n"],
5
- "mappings": "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",
6
- "names": []
4
+ "sourcesContent": ["/*\n * Checks conditions and actions of a chart: JavaScript syntax, unknown variables and events.\n * Used by the editor (to underline errors) and by the runtime (to log them when a chart starts).\n * validateChart() adds the logical problems found by the static analysis.\n */\n\nimport * as acorn from \"acorn\";\nimport * as walk from \"acorn-walk\";\nimport { analyzeChart } from \"./chart-analysis\";\nimport type { Severity } from \"./chart-analysis\";\nimport { CHANGE_HELPERS, HELPERS } from \"./chart-helpers\";\nimport { LabelSyntaxError, TIME_UNITS, formatMessage, parseTransitionLabelWithRanges } from \"./chart-model\";\nimport type { Chart, ChartSymbol } from \"./chart-model\";\nimport { ChartStructure } from \"./chart-structure\";\n\n/** Names which can be used in conditions and actions besides the chart variables */\nexport const GLOBALS = new Set([\n // the helper functions, e.g. log() and setState()\n ...Object.keys(HELPERS),\n // the time units, e.g. elapsed(min)\n ...Object.keys(TIME_UNITS),\n \"undefined\",\n \"NaN\",\n \"Infinity\",\n \"Math\",\n \"Date\",\n \"JSON\",\n \"Number\",\n \"String\",\n \"Boolean\",\n \"Array\",\n \"Object\",\n \"RegExp\",\n \"Map\",\n \"Set\",\n \"Error\",\n \"parseInt\",\n \"parseFloat\",\n \"isNaN\",\n \"isFinite\",\n \"encodeURIComponent\",\n \"decodeURIComponent\",\n]);\n\n/** Fields of a state which contain actions */\nconst STATE_ACTIONS = [\"entry\", \"during\", \"exit\"] as const;\n\nexport type CodeKind = \"expression\" | \"statements\";\n\n/** A problem at a position in a checked text, e.g. an unknown variable in an action */\nexport interface Diagnostic {\n /** Position in the checked text: index of the first character */\n from: number;\n /** Position in the checked text: index after the last character */\n to: number;\n /** How severe the problem is */\n severity: Severity;\n /** English message */\n message: string;\n /** Message with %s placeholders, used to translate it */\n key: string;\n /** Values for the %s placeholders */\n args: string[];\n}\n\nfunction createDiagnostic(from: number, to: number, severity: Severity, key: string, args: string[] = []): Diagnostic {\n return { from, to, severity, key, args, message: formatMessage(key, args) };\n}\n\n/**\n * Parses JavaScript and converts syntax errors to diagnostics\n *\n * @param code The code to parse\n * @param kind Expression or statements\n */\nfunction parseCode(code: string, kind: CodeKind): { ast: acorn.Node } | { diagnostic: Diagnostic } {\n try {\n if (kind === \"statements\") {\n return { ast: acorn.parse(code, { ecmaVersion: \"latest\", sourceType: \"script\" }) };\n }\n const ast = acorn.parseExpressionAt(code, 0, { ecmaVersion: \"latest\" });\n const rest = code.substring(ast.end);\n if (rest.trim()) {\n const from = ast.end + rest.length - rest.trimStart().length;\n return { diagnostic: createDiagnostic(from, code.length, \"error\", 'Unexpected \"%s\"', [rest.trim()]) };\n }\n return { ast };\n } catch (err) {\n const pos = typeof err.pos === \"number\" ? Math.min(err.pos, code.length) : 0;\n // acorn adds the position \"(line:column)\" to its messages, it is shown by the underline anyway\n const message = String(err.message).replace(/\\s*\\(\\d+:\\d+\\)$/, \"\");\n // underline the token at the error position, or the last character if the code ends unexpectedly\n const from = pos < code.length ? pos : Math.max(0, code.length - 1);\n const token = /^(\\w+|\\S)/.exec(code.substring(from));\n const to = Math.min(code.length, from + (token ? token[0].length : 1));\n return { diagnostic: createDiagnostic(from, to, \"error\", \"Syntax error: %s\", [message]) };\n }\n}\n\n/**\n * Checks a condition or action\n *\n * @param code The code to check\n * @param kind Condition (expression) or action (statements)\n * @param symbols The variables of the chart\n * @param offset Position of the code in the text, added to all positions\n * @param functionNames Names declared in the function blocks of the chart\n */\nexport function validateCode(\n code: string | undefined,\n kind: CodeKind,\n symbols: ChartSymbol[],\n offset = 0,\n functionNames: string[] = [],\n): Diagnostic[] {\n if (!code?.trim()) {\n return [];\n }\n const parsed = parseCode(code, kind);\n if (\"diagnostic\" in parsed) {\n const d = parsed.diagnostic;\n return [{ ...d, from: d.from + offset, to: d.to + offset }];\n }\n\n /** names declared in the code itself (let, const, function parameters, ...) */\n const declared = new Set<string>();\n const references: { node: acorn.Identifier; write: boolean }[] = [];\n walk.fullAncestor(parsed.ast, (node, _state, ancestors, type) => {\n if (node.type !== \"Identifier\") {\n return;\n }\n const parent = ancestors[ancestors.length - 2] as acorn.AnyNode | undefined;\n if (type === \"VariablePattern\") {\n // acorn-walk uses \"VariablePattern\" for declarations and for assignment targets\n if (parent?.type === \"AssignmentExpression\" && parent.left === node) {\n references.push({ node, write: true });\n } else {\n declared.add(node.name);\n }\n } else {\n references.push({ node, write: parent?.type === \"UpdateExpression\" });\n }\n });\n\n const inputs = new Set(symbols.filter(s => s.scope === \"input\").map(s => s.name));\n const symbolNames = new Set(symbols.map(s => s.name));\n const known = new Set([...symbolNames, ...functionNames]);\n const diagnostics: Diagnostic[] = [];\n\n // the change detection needs a variable of the chart, not any value\n walk.simple(parsed.ast, {\n CallExpression(node) {\n if (node.callee.type !== \"Identifier\") {\n return;\n }\n if (node.callee.name === \"getState\" && !declared.has(\"getState\")) {\n // the states are read at the start, so their IDs must be known\n const arg = node.arguments[0];\n if (!(arg?.type === \"Literal\" && typeof arg.value === \"string\" && arg.value.trim())) {\n const { start, end } = arg ?? node;\n diagnostics.push(\n createDiagnostic(\n start + offset,\n end + offset,\n \"error\",\n 'getState() needs the ID of the state as text, e.g. getState(\"0_userdata.0.temperature\")',\n ),\n );\n }\n return;\n }\n if (!CHANGE_HELPERS.has(node.callee.name)) {\n return;\n }\n const arg = node.arguments[0];\n let valid;\n if (arg?.type === \"Identifier\") {\n // an unknown name is already reported as unknown variable\n valid =\n symbolNames.has(arg.name) ||\n !(known.has(arg.name) || declared.has(arg.name) || GLOBALS.has(arg.name));\n } else {\n valid = arg?.type === \"Literal\" && typeof arg.value === \"string\" && symbolNames.has(arg.value);\n }\n if (!valid) {\n const { start, end } = arg ?? node;\n diagnostics.push(\n createDiagnostic(\n start + offset,\n end + offset,\n \"error\",\n \"%s() needs a variable of the chart as first argument\",\n [node.callee.name],\n ),\n );\n }\n },\n });\n for (const { node, write } of references) {\n const from = node.start + offset;\n const to = node.end + offset;\n if (!known.has(node.name) && !declared.has(node.name) && !GLOBALS.has(node.name)) {\n diagnostics.push(createDiagnostic(from, to, \"error\", 'Unknown variable \"%s\"', [node.name]));\n } else if (write && inputs.has(node.name) && !declared.has(node.name)) {\n diagnostics.push(\n createDiagnostic(from, to, \"warning\", 'Input \"%s\" is overwritten on its next change', [node.name]),\n );\n }\n }\n return diagnostics;\n}\n\n/**\n * Checks a transition label: its structure, the event and the JavaScript of condition and action\n *\n * @param label The label text\n * @param symbols The variables of the chart\n * @param functionNames Names declared in the function blocks of the chart\n */\nexport function validateTransitionLabel(\n label: string | undefined,\n symbols: ChartSymbol[],\n functionNames: string[] = [],\n): Diagnostic[] {\n let parsed;\n try {\n parsed = parseTransitionLabelWithRanges(label);\n } catch (err) {\n if (err instanceof LabelSyntaxError) {\n return [createDiagnostic(err.from, err.to, \"error\", err.key, err.args)];\n }\n throw err;\n }\n const { label: parts, ranges } = parsed;\n\n const diagnostics: Diagnostic[] = [];\n const isInput = (name: string): boolean => symbols.some(s => s.scope === \"input\" && s.name === name);\n if (parts.event && ranges.event && !isInput(parts.event)) {\n diagnostics.push(\n createDiagnostic(\n ranges.event.from,\n ranges.event.to,\n \"error\",\n 'Unknown event \"%s\", use the name of an input',\n [parts.event],\n ),\n );\n }\n const temporal = parts.temporal;\n if (temporal?.event && ranges.temporalEvent && !isInput(temporal.event)) {\n diagnostics.push(\n createDiagnostic(\n ranges.temporalEvent.from,\n ranges.temporalEvent.to,\n \"error\",\n 'Unknown input \"%s\", use a time unit (msec, sec, min) or the name of an input',\n [temporal.event],\n ),\n );\n }\n if (temporal?.condition && ranges.temporalCondition) {\n diagnostics.push(\n ...validateCode(temporal.condition, \"expression\", symbols, ranges.temporalCondition.from, functionNames),\n );\n }\n if (parts.condition && ranges.condition) {\n diagnostics.push(...validateCode(parts.condition, \"expression\", symbols, ranges.condition.from, functionNames));\n }\n if (parts.action && ranges.action) {\n diagnostics.push(...validateCode(parts.action, \"statements\", symbols, ranges.action.from, functionNames));\n }\n return diagnostics;\n}\n\n/** A name declared at the top level of a function block */\nexport interface Declaration {\n /** The declared name */\n name: string;\n /** e.g. \"clamp(x, lo, hi)\", null if it is no function */\n signature: string | null;\n /** Position of the name in the code: index of the first character */\n from: number;\n /** Position of the name in the code: index after the last character */\n to: number;\n}\n\n/**\n * Finds the names declared at the top level of a function block: functions, but also constants etc.\n *\n * @param code The code of the function block\n * @returns empty if the code has syntax errors\n */\nexport function getDeclarations(code: string | undefined): Declaration[] {\n if (!code?.trim()) {\n return [];\n }\n const parsed = parseCode(code, \"statements\");\n if (\"diagnostic\" in parsed) {\n return [];\n }\n const signature = (fn: acorn.Function, name: string): string =>\n `${name}(${fn.params.map(p => code.substring(p.start, p.end)).join(\", \")})`;\n\n const declarations: Declaration[] = [];\n for (const node of (parsed.ast as acorn.Program).body) {\n if (node.type === \"FunctionDeclaration\" && node.id) {\n const { name, start, end } = node.id;\n declarations.push({ name, signature: signature(node, name), from: start, to: end });\n } else if (node.type === \"VariableDeclaration\") {\n for (const declarator of node.declarations) {\n if (declarator.id.type !== \"Identifier\") {\n continue;\n }\n const { name, start, end } = declarator.id;\n const init = declarator.init;\n const isFunction = init?.type === \"ArrowFunctionExpression\" || init?.type === \"FunctionExpression\";\n declarations.push({ name, signature: isFunction ? signature(init, name) : null, from: start, to: end });\n }\n }\n }\n return declarations;\n}\n\n/**\n * Names declared in all function blocks of a chart\n *\n * @param chart The chart\n */\nexport function getFunctionNames(chart: Chart): string[] {\n return (chart.functions || []).flatMap(fn => getDeclarations(fn.code).map(d => d.name));\n}\n\n/**\n * Prepares the calls of helpers which need more than the values of their arguments:\n * - hasChanged(), hasChangedFrom() and hasChangedTo() need the name of the variable in their first argument,\n * it is replaced by the name in quotes\n * - duration() measures each call on its own, an ID of the call is added as first argument\n * - getState() reads the states which are loaded at the start, their IDs are collected\n *\n * @param code The code\n * @param kind Expression or statements\n * @param createCallId Creates the ID of a duration() call\n * @returns the changed code, the names of the variables and the IDs of the states read by getState(),\n * the unchanged code if it has syntax errors\n */\nexport function prepareHelperCalls(\n code: string,\n kind: CodeKind,\n createCallId: () => string,\n): { code: string; names: string[]; stateIds: string[] } {\n const parsed = code.trim() ? parseCode(code, kind) : null;\n if (!parsed || \"diagnostic\" in parsed) {\n return { code, names: [], stateIds: [] };\n }\n const names = new Set<string>();\n const stateIds = new Set<string>();\n /** the text replacing the code from start to end */\n const replacements: { start: number; end: number; text: string }[] = [];\n walk.simple(parsed.ast, {\n CallExpression(node) {\n if (node.callee.type !== \"Identifier\") {\n return;\n }\n const arg = node.arguments[0];\n if (node.callee.name === \"getState\") {\n if (arg?.type === \"Literal\" && typeof arg.value === \"string\") {\n stateIds.add(arg.value);\n }\n } else if (node.callee.name === \"duration\") {\n // after the opening bracket\n const at = code.indexOf(\"(\", node.callee.end) + 1;\n const id = JSON.stringify(createCallId());\n replacements.push({ start: at, end: at, text: node.arguments.length ? `${id}, ` : id });\n } else if (CHANGE_HELPERS.has(node.callee.name)) {\n if (arg?.type === \"Identifier\") {\n replacements.push({ start: arg.start, end: arg.end, text: JSON.stringify(arg.name) });\n names.add(arg.name);\n } else if (arg?.type === \"Literal\" && typeof arg.value === \"string\") {\n names.add(arg.value);\n }\n }\n },\n });\n let result = code;\n for (const { start, end, text } of replacements.sort((a, b) => b.start - a.start)) {\n result = result.substring(0, start) + text + result.substring(end);\n }\n return { code: result, names: [...names], stateIds: [...stateIds] };\n}\n\n/** A problem of a chart and where it is */\nexport interface ChartProblem {\n /** What the problem is about */\n kind: \"state\" | \"transition\" | \"function\" | \"symbol\";\n /** ID of the state, transition or function block, name of the variable */\n id: string;\n /** \"entry\", \"during\", \"exit\", \"label\" or \"code\", empty for problems of the state or variable as a whole */\n field: string;\n /** The problem and its position in the field */\n diagnostic: Diagnostic;\n}\n\n/**\n * Checks all actions, transition labels and function blocks of a chart, and analyzes its logic\n *\n * @param chart The chart\n * @param options Options\n * @param options.analyze false to check only the code, without the static analysis\n */\nexport function validateChart(chart: Chart, options: { analyze?: boolean } = {}): ChartProblem[] {\n const functionNames = getFunctionNames(chart);\n const problems: ChartProblem[] = [];\n for (const state of chart.states) {\n for (const field of STATE_ACTIONS) {\n for (const diagnostic of validateCode(state[field], \"statements\", chart.symbols, 0, functionNames)) {\n problems.push({ kind: \"state\", id: state.id, field, diagnostic });\n }\n }\n }\n const structure = new ChartStructure(chart);\n for (const transition of chart.transitions) {\n for (const diagnostic of validateTransitionLabel(transition.label, chart.symbols, functionNames)) {\n problems.push({ kind: \"transition\", id: transition.id, field: \"label\", diagnostic });\n }\n if (structure.getCrossedParallel(transition) !== undefined) {\n problems.push({\n kind: \"transition\",\n id: transition.id,\n field: \"label\",\n // parallel states are active at the same time, there is nothing to switch between\n diagnostic: createDiagnostic(0, 0, \"error\", \"Transitions between parallel states are not possible\"),\n });\n }\n }\n\n // a name may only be declared once in all function blocks, and not hide a variable\n const symbolNames = new Set(chart.symbols.map(s => s.name));\n const seen = new Set<string>();\n for (const fn of chart.functions || []) {\n for (const diagnostic of validateCode(fn.code, \"statements\", chart.symbols, 0, functionNames)) {\n problems.push({ kind: \"function\", id: fn.id, field: \"code\", diagnostic });\n }\n for (const { name, from, to } of getDeclarations(fn.code)) {\n let key = null;\n if (seen.has(name)) {\n key = '\"%s\" is declared in more than one function block';\n } else if (symbolNames.has(name) || GLOBALS.has(name)) {\n key = '\"%s\" is already the name of a variable';\n }\n if (key) {\n problems.push({\n kind: \"function\",\n id: fn.id,\n field: \"code\",\n diagnostic: createDiagnostic(from, to, \"error\", key, [name]),\n });\n }\n seen.add(name);\n }\n }\n\n for (const { kind, id, severity, key, args } of options.analyze === false ? [] : analyzeChart(chart)) {\n problems.push({\n kind,\n id,\n field: kind === \"transition\" ? \"label\" : \"\",\n diagnostic: createDiagnostic(0, 0, severity, key, args),\n });\n }\n return problems;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["arg"]
7
7
  }