Skip to content
This repository was archived by the owner on Oct 21, 2025. It is now read-only.

Commit 3ce2f47

Browse files
authored
Merge pull request #47 from DataFlowAnalysis/assignment
Assignment Definition Changes
2 parents c8a236b + 03127ce commit 3ce2f47

File tree

3 files changed

+451
-252
lines changed

3 files changed

+451
-252
lines changed

src/features/dfdElements/outputPortBehaviorValidation.ts

Lines changed: 189 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,23 @@ interface PortBehaviorValidationError {
2020
*/
2121
@injectable()
2222
export class PortBehaviorValidator {
23-
// Regex that validates a set statement.
23+
// Regex that validates assignments
24+
// Matches "Assignment({input_Pins};TERM_REGEX;{out_Label})"
25+
private static readonly ASSIGNMENT_REGEX =
26+
/^Assignment\(\{(([A-Za-z0-9_][A-Za-z0-9_\|]+(,\s*[A-Za-z0-9_\|]+)*)?)\};(\s*|!|TRUE|FALSE|\|\||&&|\(|\)|([A-Za-z0-9_]*\.[A-Za-z0-9_]*))+;\{(((([A-Za-z0-9_]+)\.[A-Za-z0-9_]+)+(,\s*([A-Za-z0-9_]+\.[A-Za-z0-9_]+))*)?)\}\)+$/;
27+
28+
// Regex that validates forwarding
29+
// Matches "Forwarding({input_pins})"
30+
private static readonly FORWARDING_REGEX =
31+
/^Forwarding\(\{[A-Za-z0-9_][A-Za-z0-9_\|]+(,\s*[A-Za-z0-9_][A-Za-z0-9_\|]+)*\}\)$/;
32+
33+
// Regex that validates a term
2434
// Has the label type and label value that should be set as capturing groups.
25-
private static readonly SET_REGEX =
26-
/^set +([A-Za-z][A-Za-z0-9_]*)\.([A-Za-z][A-Za-z0-9_]*) *= *(?: +|!|TRUE|FALSE|\|\||&&|\(|\)|[A-Za-z][A-Za-z0-9_\|]*(?:\.[A-Za-z][A-Za-z0-9_]*){2})+$/;
27-
// Regex that is used to extract all inputs, their label types and label values from a set statement.
28-
// Each input is a match with the input name, label type and label value as capturing groups.
29-
private static readonly SET_REGEX_EXPRESSION_INPUTS =
30-
/([A-Za-z][A-Za-z0-9_\|]*)\.([A-Za-z][A-Za-z0-9_]*)\.([A-Za-z][A-Za-z0-9_]*)/g;
35+
private static readonly TERM_REGEX =
36+
/^(\s*|!|TRUE|FALSE|\|\||&&|\(|\)|([A-Za-z0-9_]+\.[A-Za-z0-9_]+(?![A-Za-z0-9_]*\.[A-Za-z0-9_]*)))+$/g;
37+
38+
private static readonly LABEL_REGEX = /([A-Za-z0-9_]+)\.([A-Za-z0-9_]+)/g;
39+
3140
// Regex matching alphanumeric characters.
3241
public static readonly REGEX_ALPHANUMERIC = /[A-Za-z0-9_\|]+/;
3342

@@ -74,11 +83,11 @@ export class PortBehaviorValidator {
7483
return;
7584
}
7685

77-
if (line.startsWith("forward")) {
86+
if (line.startsWith("Forwarding")) {
7887
return this.validateForwardStatement(line, lineNumber, port);
7988
}
8089

81-
if (line.startsWith("set")) {
90+
if (line.startsWith("Assignment")) {
8291
return this.validateSetStatement(line, lineNumber, port);
8392
}
8493

@@ -95,7 +104,17 @@ export class PortBehaviorValidator {
95104
lineNumber: number,
96105
port: DfdOutputPortImpl,
97106
): PortBehaviorValidationError[] | undefined {
98-
const inputsString = line.substring("forward".length);
107+
const match = line.match(PortBehaviorValidator.FORWARDING_REGEX);
108+
if (!match) {
109+
return [
110+
{
111+
line: lineNumber,
112+
message: "invalid forwarding(Template:Forwarding({in_ports})",
113+
},
114+
];
115+
}
116+
117+
const inputsString = line.substring("Forwarding({".length, line.length - 2);
99118
const inputs = inputsString.split(",").map((input) => input.trim());
100119
if (inputs.filter((input) => input !== "").length === 0) {
101120
return [
@@ -197,37 +216,12 @@ export class PortBehaviorValidator {
197216
lineNumber: number,
198217
port: DfdOutputPortImpl,
199218
): PortBehaviorValidationError[] | undefined {
200-
const match = line.match(PortBehaviorValidator.SET_REGEX);
219+
const match = line.match(PortBehaviorValidator.ASSIGNMENT_REGEX);
201220
if (!match) {
202221
return [
203222
{
204223
line: lineNumber,
205-
message: "invalid set statement",
206-
},
207-
];
208-
}
209-
210-
// Check that the label type and value that this statement tries to set are valid.
211-
const setLabelType = match[1];
212-
const setLabelValue = match[2];
213-
const labelType = this.labelTypeRegistry?.getLabelTypes().find((type) => type.name === setLabelType);
214-
if (!labelType) {
215-
return [
216-
{
217-
line: lineNumber,
218-
message: `unknown label type: ${setLabelType}`,
219-
colStart: line.indexOf(setLabelType),
220-
colEnd: line.indexOf(setLabelType) + setLabelType.length,
221-
},
222-
];
223-
}
224-
if (!labelType.values.find((value) => value.text === setLabelValue)) {
225-
return [
226-
{
227-
line: lineNumber,
228-
message: `unknown label value of label type ${setLabelType}: ${setLabelValue}`,
229-
colStart: line.indexOf(setLabelValue),
230-
colEnd: line.indexOf(setLabelValue) + setLabelValue.length,
224+
message: "invalid assignment(Template:Assignment({in_ports}; term; {out_label})",
231225
},
232226
];
233227
}
@@ -246,7 +240,7 @@ export class PortBehaviorValidator {
246240
return [
247241
{
248242
line: lineNumber,
249-
message: "invalid set statement: missing opening parenthesis",
243+
message: "invalid assignment: missing opening parenthesis",
250244
colStart: strIdx,
251245
colEnd: strIdx + 1,
252246
},
@@ -258,60 +252,41 @@ export class PortBehaviorValidator {
258252
return [
259253
{
260254
line: lineNumber,
261-
message: "invalid set statement: missing closing parenthesis",
255+
message: "invalid assignment: missing closing parenthesis",
262256
},
263257
];
264258
}
265259

266260
// Extract all used inputs, label types and the corresponding label values.
267-
const expression = line.split("=")[1].trim(); // get everything after the =
268-
if (expression.length === 0) {
261+
var term = line.split(";")[1].trim(); // get everything after the ;
262+
if (term.length === 0) {
269263
return [
270264
{
271265
line: lineNumber,
272-
message: "invalid set statement: missing expression",
266+
message: "invalid assignment: missing term",
273267
},
274268
];
275269
}
270+
if (term.indexOf(";") !== -1) {
271+
term = term.split(";")[0];
272+
}
276273

277-
const matches = [...expression.matchAll(PortBehaviorValidator.SET_REGEX_EXPRESSION_INPUTS)];
278-
279-
const node = port.parent;
280-
if (!(node instanceof DfdNodeImpl)) {
281-
throw new Error("Expected port parent to be a DfdNodeImpl.");
274+
const termMatch = term.match(PortBehaviorValidator.TERM_REGEX);
275+
if (!termMatch) {
276+
return [
277+
{
278+
line: lineNumber,
279+
message: "invalid term",
280+
},
281+
];
282282
}
283-
const availableInputs = node.getAvailableInputs();
284283

285-
// Check for each input access that the input exists and that the label type and value are valid.
284+
const matches = [...term.matchAll(PortBehaviorValidator.LABEL_REGEX)];
286285
const inputAccessErrors = [];
287-
for (const inputMatch of matches) {
288-
const inputName = inputMatch[1];
289-
const inputLabelType = inputMatch[2];
290-
const inputLabelValue = inputMatch[3];
291-
292-
if (!availableInputs.includes(inputName)) {
293-
// Find all occurrences of the unavailable input.
294-
let idx = line.indexOf(inputName);
295-
while (idx !== -1) {
296-
// Check that this is not a substring of another input.
297-
if (
298-
// before must not be alphanumeric => start of this string must be the beginning of the input name
299-
line[idx - 1]?.match(PortBehaviorValidator.REGEX_ALPHANUMERIC) &&
300-
line[idx + inputName.length] === "." // must be followed by a dot to access the label type of the input
301-
) {
302-
inputAccessErrors.push({
303-
line: lineNumber,
304-
message: `invalid/unknown input: ${inputName}`,
305-
colStart: idx,
306-
colEnd: idx + inputName.length,
307-
});
308-
}
309-
310-
idx = line.indexOf(inputName, idx + 1);
311-
}
312286

313-
continue;
314-
}
287+
for (const inputMatch of matches) {
288+
const inputLabelType = inputMatch[1];
289+
const inputLabelValue = inputMatch[2];
315290

316291
const inputLabelTypeObject = this.labelTypeRegistry
317292
?.getLabelTypes()
@@ -335,7 +310,11 @@ export class PortBehaviorValidator {
335310

336311
idx = line.indexOf(inputLabelType, idx + 1);
337312
}
338-
} else if (!inputLabelTypeObject.values.find((value) => value.text === inputLabelValue)) {
313+
} else if (
314+
inputLabelValue === undefined ||
315+
inputLabelValue === "" ||
316+
!inputLabelTypeObject.values.find((value) => value.text === inputLabelValue)
317+
) {
339318
let idx = line.indexOf(inputLabelValue);
340319
while (idx !== -1) {
341320
// Check that this is not a substring of another label value.
@@ -357,8 +336,140 @@ export class PortBehaviorValidator {
357336
idx = line.indexOf(inputLabelValue, idx + 1);
358337
}
359338
}
339+
340+
console.log(inputMatch);
341+
342+
if (inputMatch[3] !== undefined) {
343+
inputAccessErrors.push({
344+
line: lineNumber,
345+
message: `invalid label definition`,
346+
});
347+
}
348+
}
349+
350+
const node = port.parent;
351+
if (!(node instanceof DfdNodeImpl)) {
352+
throw new Error("Expected port parent to be a DfdNodeImpl.");
353+
}
354+
const availableInputs = node.getAvailableInputs();
355+
356+
const innerContent = line.substring("Assignment(".length, line.length - 1);
357+
358+
// Step 2: Split by the semicolons to separate the blocks
359+
const parts = innerContent.split(";").map((part) => part.trim());
360+
361+
const inPorts = parts[0]
362+
.substring(1, parts[0].length - 1)
363+
.split(",")
364+
.map((variable) => variable.trim());
365+
const outLabel = parts[2]
366+
.substring(1, parts[2].length - 1)
367+
.split(",")
368+
.map((variable) => variable.trim());
369+
370+
// Check for each input access that the input exists and that the label type and value are valid.
371+
372+
for (const inPortName of inPorts) {
373+
if (!availableInputs.includes(inPortName) && inPortName !== "") {
374+
// Find all occurrences of the unavailable input.
375+
let idx = line.indexOf(inPortName);
376+
inputAccessErrors.push({
377+
line: lineNumber,
378+
message: `invalid/unknown input: ${inPortName}`,
379+
colStart: idx,
380+
colEnd: idx + inPortName.length,
381+
});
382+
383+
continue;
384+
}
385+
}
386+
387+
for (const typeValuePair of outLabel) {
388+
if (typeValuePair === "") continue;
389+
390+
const inputLabelType = typeValuePair.split(".")[0].trim();
391+
const inputLabelTypeObject = this.labelTypeRegistry
392+
?.getLabelTypes()
393+
.find((type) => type.name === inputLabelType);
394+
if (!inputLabelTypeObject) {
395+
let idx = line.indexOf(inputLabelType);
396+
while (idx !== -1) {
397+
// Check that this is not a substring of another label type.
398+
if (
399+
// must start after a dot and end before a dot
400+
line[idx - 1] === "." &&
401+
line[idx + inputLabelType.length] === "."
402+
) {
403+
inputAccessErrors.push({
404+
line: lineNumber,
405+
message: `unknown label type: ${inputLabelType}`,
406+
colStart: idx,
407+
colEnd: idx + inputLabelType.length,
408+
});
409+
}
410+
411+
idx = line.indexOf(inputLabelType, idx + 1);
412+
}
413+
}
414+
415+
if (typeValuePair.indexOf(".") !== -1) {
416+
if (typeValuePair.split(".")[1] === null || typeValuePair.split(".")[1] === "") continue;
417+
const inputLabelValue = typeValuePair.split(".")[1].trim();
418+
419+
const inputLabelTypeObject = this.labelTypeRegistry
420+
?.getLabelTypes()
421+
.find((type) => type.name === inputLabelType);
422+
if (!inputLabelTypeObject) {
423+
let idx = line.indexOf(inputLabelType);
424+
while (idx !== -1) {
425+
// Check that this is not a substring of another label type.
426+
if (
427+
// must start after a dot and end before a dot
428+
line[idx - 1] === "." &&
429+
line[idx + inputLabelType.length] === "."
430+
) {
431+
inputAccessErrors.push({
432+
line: lineNumber,
433+
message: `unknown label type: ${inputLabelType}`,
434+
colStart: idx,
435+
colEnd: idx + inputLabelType.length,
436+
});
437+
}
438+
439+
idx = line.indexOf(inputLabelType, idx + 1);
440+
}
441+
} else if (!inputLabelTypeObject.values.find((value) => value.text === inputLabelValue)) {
442+
let idx = line.indexOf(inputLabelValue);
443+
while (idx !== -1) {
444+
// Check that this is not a substring of another label value.
445+
if (
446+
// must start after a dot and end at the end of the alphanumeric text
447+
line[idx - 1] === "." &&
448+
// Might be at the end of the line
449+
(!line[idx + inputLabelValue.length] ||
450+
!line[idx + inputLabelValue.length].match(PortBehaviorValidator.REGEX_ALPHANUMERIC))
451+
) {
452+
inputAccessErrors.push({
453+
line: lineNumber,
454+
message: `unknown label value of label type ${inputLabelType}: ${inputLabelValue}`,
455+
colStart: idx,
456+
colEnd: idx + inputLabelValue.length,
457+
});
458+
}
459+
460+
idx = line.indexOf(inputLabelValue, idx + 1);
461+
}
462+
}
463+
}
464+
465+
if (typeValuePair.split(".")[2] !== undefined) {
466+
inputAccessErrors.push({
467+
line: lineNumber,
468+
message: `invalid label definition`,
469+
});
470+
}
360471
}
361472

362-
return inputAccessErrors.length > 0 ? inputAccessErrors : undefined;
473+
return inputAccessErrors.length > 0 ? inputAccessErrors : [];
363474
}
364475
}

0 commit comments

Comments
 (0)