feat(cdlEventHandler): detect TOGGLE Off and emit *_defined globals
Record parameters marked with "TOGGLE Off" by adding an optional toggleOffParameterNames field and including an extra <var>_defined global for each such parameter in createCdlEventHandlerSnippet. To support this, the parser was changed from a brace-delta approach to a token-based scanner so nesting and the association between a PARAM and its TOGGLE can be tracked reliably. Added unit tests that verify detection, case-insensitivity, and ignoring TOGGLE occurrences inside comments/strings or other events.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
export interface CdlEventHandler {
|
export interface CdlEventHandler {
|
||||||
eventName: string
|
eventName: string
|
||||||
parameterNames: string[]
|
parameterNames: string[]
|
||||||
|
toggleOffParameterNames?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
function structuralCode(line: string): string {
|
function structuralCode(line: string): string {
|
||||||
@@ -33,18 +34,6 @@ function structuralCode(line: string): string {
|
|||||||
return result
|
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(
|
export function cdlEventHandlerAtLine(
|
||||||
source: string,
|
source: string,
|
||||||
declarationLine: number
|
declarationLine: number
|
||||||
@@ -61,35 +50,44 @@ export function cdlEventHandlerAtLine(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const parameterNames: string[] = []
|
const parameterNames: string[] = []
|
||||||
|
const toggleOffParameterNames: string[] = []
|
||||||
|
let currentParameter: string | undefined
|
||||||
let eventOpened = false
|
let eventOpened = false
|
||||||
let depth = 0
|
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 line = lines[lineNumber]
|
||||||
const code = structuralCode(line)
|
const code = structuralCode(line)
|
||||||
|
|
||||||
if (eventOpened && depth === 1) {
|
const tokens = code.match(/[{}]|[^\s{}]+/g) ?? []
|
||||||
const parameterMatch = /^\s*PARAM\s+([^\s{]+)/.exec(code)
|
for (let index = 0; index < tokens.length; index++) {
|
||||||
if (parameterMatch) {
|
const token = tokens[index]
|
||||||
parameterNames.push(parameterMatch[1])
|
if (token === "{") {
|
||||||
}
|
eventOpened = true
|
||||||
}
|
depth++
|
||||||
|
} else if (token === "}" && eventOpened) {
|
||||||
const delta = braceDelta(line)
|
depth--
|
||||||
if (!eventOpened && delta > 0) {
|
if (depth === 1) currentParameter = undefined
|
||||||
eventOpened = true
|
if (depth <= 0) break eventLines
|
||||||
}
|
} else if (depth === 1 && token === "PARAM") {
|
||||||
if (eventOpened) {
|
const name = tokens[index + 1]
|
||||||
depth += delta
|
if (name && name !== "{" && name !== "}") {
|
||||||
if (depth <= 0) {
|
currentParameter = name
|
||||||
break
|
parameterNames.push(name)
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
} else if (depth === 2 && currentParameter && token === "TOGGLE") {
|
||||||
|
if (tokens[index + 1]?.toLowerCase() === "off") {
|
||||||
|
toggleOffParameterNames.push(currentParameter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
eventName: eventMatch[1],
|
eventName: eventMatch[1],
|
||||||
parameterNames
|
parameterNames,
|
||||||
|
toggleOffParameterNames
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,8 +100,12 @@ function momVariableName(parameterName: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createCdlEventHandlerSnippet(handler: CdlEventHandler): string {
|
export function createCdlEventHandlerSnippet(handler: CdlEventHandler): string {
|
||||||
|
const toggleOffParameters = new Set(handler.toggleOffParameterNames ?? [])
|
||||||
const globals = [
|
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} {`]
|
const lines = [`proc ${momEventName(handler.eventName)} {args} {`]
|
||||||
|
|
||||||
|
|||||||
@@ -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/)
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user