Skip to content

Commit

Permalink
Merge pull request #47 from DataFlowAnalysis/assignment
Browse files Browse the repository at this point in the history
Assignment Definition Changes
  • Loading branch information
01Parzival10 authored Oct 16, 2024
2 parents c8a236b + 03127ce commit 3ce2f47
Show file tree
Hide file tree
Showing 3 changed files with 451 additions and 252 deletions.
267 changes: 189 additions & 78 deletions src/features/dfdElements/outputPortBehaviorValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,23 @@ interface PortBehaviorValidationError {
*/
@injectable()
export class PortBehaviorValidator {
// Regex that validates a set statement.
// Regex that validates assignments
// Matches "Assignment({input_Pins};TERM_REGEX;{out_Label})"
private static readonly ASSIGNMENT_REGEX =
/^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_]+))*)?)\}\)+$/;

// Regex that validates forwarding
// Matches "Forwarding({input_pins})"
private static readonly FORWARDING_REGEX =
/^Forwarding\(\{[A-Za-z0-9_][A-Za-z0-9_\|]+(,\s*[A-Za-z0-9_][A-Za-z0-9_\|]+)*\}\)$/;

// Regex that validates a term
// Has the label type and label value that should be set as capturing groups.
private static readonly SET_REGEX =
/^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})+$/;
// Regex that is used to extract all inputs, their label types and label values from a set statement.
// Each input is a match with the input name, label type and label value as capturing groups.
private static readonly SET_REGEX_EXPRESSION_INPUTS =
/([A-Za-z][A-Za-z0-9_\|]*)\.([A-Za-z][A-Za-z0-9_]*)\.([A-Za-z][A-Za-z0-9_]*)/g;
private static readonly TERM_REGEX =
/^(\s*|!|TRUE|FALSE|\|\||&&|\(|\)|([A-Za-z0-9_]+\.[A-Za-z0-9_]+(?![A-Za-z0-9_]*\.[A-Za-z0-9_]*)))+$/g;

private static readonly LABEL_REGEX = /([A-Za-z0-9_]+)\.([A-Za-z0-9_]+)/g;

// Regex matching alphanumeric characters.
public static readonly REGEX_ALPHANUMERIC = /[A-Za-z0-9_\|]+/;

Expand Down Expand Up @@ -74,11 +83,11 @@ export class PortBehaviorValidator {
return;
}

if (line.startsWith("forward")) {
if (line.startsWith("Forwarding")) {
return this.validateForwardStatement(line, lineNumber, port);
}

if (line.startsWith("set")) {
if (line.startsWith("Assignment")) {
return this.validateSetStatement(line, lineNumber, port);
}

Expand All @@ -95,7 +104,17 @@ export class PortBehaviorValidator {
lineNumber: number,
port: DfdOutputPortImpl,
): PortBehaviorValidationError[] | undefined {
const inputsString = line.substring("forward".length);
const match = line.match(PortBehaviorValidator.FORWARDING_REGEX);
if (!match) {
return [
{
line: lineNumber,
message: "invalid forwarding(Template:Forwarding({in_ports})",
},
];
}

const inputsString = line.substring("Forwarding({".length, line.length - 2);
const inputs = inputsString.split(",").map((input) => input.trim());
if (inputs.filter((input) => input !== "").length === 0) {
return [
Expand Down Expand Up @@ -197,37 +216,12 @@ export class PortBehaviorValidator {
lineNumber: number,
port: DfdOutputPortImpl,
): PortBehaviorValidationError[] | undefined {
const match = line.match(PortBehaviorValidator.SET_REGEX);
const match = line.match(PortBehaviorValidator.ASSIGNMENT_REGEX);
if (!match) {
return [
{
line: lineNumber,
message: "invalid set statement",
},
];
}

// Check that the label type and value that this statement tries to set are valid.
const setLabelType = match[1];
const setLabelValue = match[2];
const labelType = this.labelTypeRegistry?.getLabelTypes().find((type) => type.name === setLabelType);
if (!labelType) {
return [
{
line: lineNumber,
message: `unknown label type: ${setLabelType}`,
colStart: line.indexOf(setLabelType),
colEnd: line.indexOf(setLabelType) + setLabelType.length,
},
];
}
if (!labelType.values.find((value) => value.text === setLabelValue)) {
return [
{
line: lineNumber,
message: `unknown label value of label type ${setLabelType}: ${setLabelValue}`,
colStart: line.indexOf(setLabelValue),
colEnd: line.indexOf(setLabelValue) + setLabelValue.length,
message: "invalid assignment(Template:Assignment({in_ports}; term; {out_label})",
},
];
}
Expand All @@ -246,7 +240,7 @@ export class PortBehaviorValidator {
return [
{
line: lineNumber,
message: "invalid set statement: missing opening parenthesis",
message: "invalid assignment: missing opening parenthesis",
colStart: strIdx,
colEnd: strIdx + 1,
},
Expand All @@ -258,60 +252,41 @@ export class PortBehaviorValidator {
return [
{
line: lineNumber,
message: "invalid set statement: missing closing parenthesis",
message: "invalid assignment: missing closing parenthesis",
},
];
}

// Extract all used inputs, label types and the corresponding label values.
const expression = line.split("=")[1].trim(); // get everything after the =
if (expression.length === 0) {
var term = line.split(";")[1].trim(); // get everything after the ;
if (term.length === 0) {
return [
{
line: lineNumber,
message: "invalid set statement: missing expression",
message: "invalid assignment: missing term",
},
];
}
if (term.indexOf(";") !== -1) {
term = term.split(";")[0];
}

const matches = [...expression.matchAll(PortBehaviorValidator.SET_REGEX_EXPRESSION_INPUTS)];

const node = port.parent;
if (!(node instanceof DfdNodeImpl)) {
throw new Error("Expected port parent to be a DfdNodeImpl.");
const termMatch = term.match(PortBehaviorValidator.TERM_REGEX);
if (!termMatch) {
return [
{
line: lineNumber,
message: "invalid term",
},
];
}
const availableInputs = node.getAvailableInputs();

// Check for each input access that the input exists and that the label type and value are valid.
const matches = [...term.matchAll(PortBehaviorValidator.LABEL_REGEX)];
const inputAccessErrors = [];
for (const inputMatch of matches) {
const inputName = inputMatch[1];
const inputLabelType = inputMatch[2];
const inputLabelValue = inputMatch[3];

if (!availableInputs.includes(inputName)) {
// Find all occurrences of the unavailable input.
let idx = line.indexOf(inputName);
while (idx !== -1) {
// Check that this is not a substring of another input.
if (
// before must not be alphanumeric => start of this string must be the beginning of the input name
line[idx - 1]?.match(PortBehaviorValidator.REGEX_ALPHANUMERIC) &&
line[idx + inputName.length] === "." // must be followed by a dot to access the label type of the input
) {
inputAccessErrors.push({
line: lineNumber,
message: `invalid/unknown input: ${inputName}`,
colStart: idx,
colEnd: idx + inputName.length,
});
}

idx = line.indexOf(inputName, idx + 1);
}

continue;
}
for (const inputMatch of matches) {
const inputLabelType = inputMatch[1];
const inputLabelValue = inputMatch[2];

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

idx = line.indexOf(inputLabelType, idx + 1);
}
} else if (!inputLabelTypeObject.values.find((value) => value.text === inputLabelValue)) {
} else if (
inputLabelValue === undefined ||
inputLabelValue === "" ||
!inputLabelTypeObject.values.find((value) => value.text === inputLabelValue)
) {
let idx = line.indexOf(inputLabelValue);
while (idx !== -1) {
// Check that this is not a substring of another label value.
Expand All @@ -357,8 +336,140 @@ export class PortBehaviorValidator {
idx = line.indexOf(inputLabelValue, idx + 1);
}
}

console.log(inputMatch);

if (inputMatch[3] !== undefined) {
inputAccessErrors.push({
line: lineNumber,
message: `invalid label definition`,
});
}
}

const node = port.parent;
if (!(node instanceof DfdNodeImpl)) {
throw new Error("Expected port parent to be a DfdNodeImpl.");
}
const availableInputs = node.getAvailableInputs();

const innerContent = line.substring("Assignment(".length, line.length - 1);

// Step 2: Split by the semicolons to separate the blocks
const parts = innerContent.split(";").map((part) => part.trim());

const inPorts = parts[0]
.substring(1, parts[0].length - 1)
.split(",")
.map((variable) => variable.trim());
const outLabel = parts[2]
.substring(1, parts[2].length - 1)
.split(",")
.map((variable) => variable.trim());

// Check for each input access that the input exists and that the label type and value are valid.

for (const inPortName of inPorts) {
if (!availableInputs.includes(inPortName) && inPortName !== "") {
// Find all occurrences of the unavailable input.
let idx = line.indexOf(inPortName);
inputAccessErrors.push({
line: lineNumber,
message: `invalid/unknown input: ${inPortName}`,
colStart: idx,
colEnd: idx + inPortName.length,
});

continue;
}
}

for (const typeValuePair of outLabel) {
if (typeValuePair === "") continue;

const inputLabelType = typeValuePair.split(".")[0].trim();
const inputLabelTypeObject = this.labelTypeRegistry
?.getLabelTypes()
.find((type) => type.name === inputLabelType);
if (!inputLabelTypeObject) {
let idx = line.indexOf(inputLabelType);
while (idx !== -1) {
// Check that this is not a substring of another label type.
if (
// must start after a dot and end before a dot
line[idx - 1] === "." &&
line[idx + inputLabelType.length] === "."
) {
inputAccessErrors.push({
line: lineNumber,
message: `unknown label type: ${inputLabelType}`,
colStart: idx,
colEnd: idx + inputLabelType.length,
});
}

idx = line.indexOf(inputLabelType, idx + 1);
}
}

if (typeValuePair.indexOf(".") !== -1) {
if (typeValuePair.split(".")[1] === null || typeValuePair.split(".")[1] === "") continue;
const inputLabelValue = typeValuePair.split(".")[1].trim();

const inputLabelTypeObject = this.labelTypeRegistry
?.getLabelTypes()
.find((type) => type.name === inputLabelType);
if (!inputLabelTypeObject) {
let idx = line.indexOf(inputLabelType);
while (idx !== -1) {
// Check that this is not a substring of another label type.
if (
// must start after a dot and end before a dot
line[idx - 1] === "." &&
line[idx + inputLabelType.length] === "."
) {
inputAccessErrors.push({
line: lineNumber,
message: `unknown label type: ${inputLabelType}`,
colStart: idx,
colEnd: idx + inputLabelType.length,
});
}

idx = line.indexOf(inputLabelType, idx + 1);
}
} else if (!inputLabelTypeObject.values.find((value) => value.text === inputLabelValue)) {
let idx = line.indexOf(inputLabelValue);
while (idx !== -1) {
// Check that this is not a substring of another label value.
if (
// must start after a dot and end at the end of the alphanumeric text
line[idx - 1] === "." &&
// Might be at the end of the line
(!line[idx + inputLabelValue.length] ||
!line[idx + inputLabelValue.length].match(PortBehaviorValidator.REGEX_ALPHANUMERIC))
) {
inputAccessErrors.push({
line: lineNumber,
message: `unknown label value of label type ${inputLabelType}: ${inputLabelValue}`,
colStart: idx,
colEnd: idx + inputLabelValue.length,
});
}

idx = line.indexOf(inputLabelValue, idx + 1);
}
}
}

if (typeValuePair.split(".")[2] !== undefined) {
inputAccessErrors.push({
line: lineNumber,
message: `invalid label definition`,
});
}
}

return inputAccessErrors.length > 0 ? inputAccessErrors : undefined;
return inputAccessErrors.length > 0 ? inputAccessErrors : [];
}
}
Loading

0 comments on commit 3ce2f47

Please sign in to comment.