diff --git a/client/src/common/cdlEventHandler.ts b/client/src/common/cdlEventHandler.ts index b6282c9..7b750ed 100644 --- a/client/src/common/cdlEventHandler.ts +++ b/client/src/common/cdlEventHandler.ts @@ -1,6 +1,7 @@ export interface CdlEventHandler { eventName: string parameterNames: string[] + toggleOffParameterNames?: string[] } function structuralCode(line: string): string { @@ -33,18 +34,6 @@ function structuralCode(line: string): string { return result } -function braceDelta(line: string): number { - let delta = 0 - for (const character of structuralCode(line)) { - if (character === "{") { - delta += 1 - } else if (character === "}") { - delta -= 1 - } - } - return delta -} - export function cdlEventHandlerAtLine( source: string, declarationLine: number @@ -61,35 +50,44 @@ export function cdlEventHandlerAtLine( } const parameterNames: string[] = [] + const toggleOffParameterNames: string[] = [] + let currentParameter: string | undefined let eventOpened = false let depth = 0 - for (let lineNumber = declarationLine; lineNumber < lines.length; lineNumber++) { + eventLines: for (let lineNumber = declarationLine; lineNumber < lines.length; lineNumber++) { const line = lines[lineNumber] const code = structuralCode(line) - if (eventOpened && depth === 1) { - const parameterMatch = /^\s*PARAM\s+([^\s{]+)/.exec(code) - if (parameterMatch) { - parameterNames.push(parameterMatch[1]) - } - } - - const delta = braceDelta(line) - if (!eventOpened && delta > 0) { - eventOpened = true - } - if (eventOpened) { - depth += delta - if (depth <= 0) { - break + const tokens = code.match(/[{}]|[^\s{}]+/g) ?? [] + for (let index = 0; index < tokens.length; index++) { + const token = tokens[index] + if (token === "{") { + eventOpened = true + depth++ + } else if (token === "}" && eventOpened) { + depth-- + if (depth === 1) currentParameter = undefined + if (depth <= 0) break eventLines + } else if (depth === 1 && token === "PARAM") { + const name = tokens[index + 1] + if (name && name !== "{" && name !== "}") { + currentParameter = name + parameterNames.push(name) + index++ + } + } else if (depth === 2 && currentParameter && token === "TOGGLE") { + if (tokens[index + 1]?.toLowerCase() === "off") { + toggleOffParameterNames.push(currentParameter) + } } } } return { eventName: eventMatch[1], - parameterNames + parameterNames, + toggleOffParameterNames } } @@ -102,8 +100,12 @@ function momVariableName(parameterName: string): string { } export function createCdlEventHandlerSnippet(handler: CdlEventHandler): string { + const toggleOffParameters = new Set(handler.toggleOffParameterNames ?? []) const globals = [ - ...new Set(handler.parameterNames.map((parameter) => momVariableName(parameter))) + ...new Set(handler.parameterNames.flatMap((parameter) => { + const variable = momVariableName(parameter) + return toggleOffParameters.has(parameter) ? [variable, `${variable}_defined`] : [variable] + })) ] const lines = [`proc ${momEventName(handler.eventName)} {args} {`] diff --git a/test/cdlEventHandler.test.js b/test/cdlEventHandler.test.js new file mode 100644 index 0000000..6276870 --- /dev/null +++ b/test/cdlEventHandler.test.js @@ -0,0 +1,51 @@ +const assert = require("node:assert/strict") +const fs = require("node:fs") +const path = require("node:path") +const vm = require("node:vm") +const { test } = require("node:test") +const { transformSync } = require("esbuild") + +const source = fs.readFileSync(path.join(__dirname, "../client/src/common/cdlEventHandler.ts"), "utf8") +const compiled = transformSync(source, { loader: "ts", format: "cjs" }).code +const context = { module: { exports: {} } } +vm.runInNewContext(compiled, context) +const { cdlEventHandlerAtLine, createCdlEventHandlerSnippet } = context.module.exports + +test("TOGGLE Off adds the defined globals to the event handler", () => { + const cdl = `EVENT GDM_header +{ + PARAM product_status { + TYPE o + OPTIONS "Serie", "Prototyp" + } + PARAM stm_param_mpf_name + { + TYPE s + TOGGLE Off + } + PARAM stm_param_wks_path { + TYPE s + TOGGLE Off + } +}` + const snippet = createCdlEventHandlerSnippet(cdlEventHandlerAtLine(cdl, 0)) + assert.match(snippet, /global mom_stm_param_mpf_name_defined/) + assert.match(snippet, /global mom_stm_param_wks_path_defined/) + assert.match(snippet, /global mom_product_status\n/) + assert.doesNotMatch(snippet, /mom_product_status_defined/) +}) + +test("toggle detection ignores comments, strings, other events, and TOGGLE On", () => { + const cdl = `EVENT first { + PARAM plain { TYPE s UI_LABEL "TOGGLE Off { ignored }" } + PARAM enabled { TOGGLE On } + PARAM commented { TYPE s # TOGGLE Off + } + PARAM mom_disabled { TOGGLE off } + } + EVENT second { PARAM other { TOGGLE Off } }` + const snippet = createCdlEventHandlerSnippet(cdlEventHandlerAtLine(cdl, 0)) + assert.match(snippet, /global mom_disabled_defined/) + assert.equal((snippet.match(/_defined/g) ?? []).length, 1) + assert.doesNotMatch(snippet, /mom_mom_|mom_other/) +})