@@ -20,14 +20,23 @@ interface PortBehaviorValidationError {
2020 */
2121@injectable ( )
2222export 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+ / ^ A s s i g n m e n t \( \{ ( ( [ A - Z a - z 0 - 9 _ ] [ A - Z a - z 0 - 9 _ \| ] + ( , \s * [ A - Z a - z 0 - 9 _ \| ] + ) * ) ? ) \} ; ( \s * | ! | T R U E | F A L S E | \| \| | & & | \( | \) | ( [ A - Z a - z 0 - 9 _ ] * \. [ A - Z a - z 0 - 9 _ ] * ) ) + ; \{ ( ( ( ( [ A - Z a - z 0 - 9 _ ] + ) \. [ A - Z a - z 0 - 9 _ ] + ) + ( , \s * ( [ A - Z a - z 0 - 9 _ ] + \. [ A - Z a - z 0 - 9 _ ] + ) ) * ) ? ) \} \) + $ / ;
27+
28+ // Regex that validates forwarding
29+ // Matches "Forwarding({input_pins})"
30+ private static readonly FORWARDING_REGEX =
31+ / ^ F o r w a r d i n g \( \{ [ A - Z a - z 0 - 9 _ ] [ A - Z a - z 0 - 9 _ \| ] + ( , \s * [ A - Z a - z 0 - 9 _ ] [ A - Z a - z 0 - 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- / ^ s e t + ( [ A - Z a - z ] [ A - Z a - z 0 - 9 _ ] * ) \. ( [ A - Z a - z ] [ A - Z a - z 0 - 9 _ ] * ) * = * (?: + | ! | T R U E | F A L S E | \| \| | & & | \( | \) | [ A - Z a - z ] [ A - Z a - z 0 - 9 _ \| ] * (?: \. [ A - Z a - z ] [ A - Z a - z 0 - 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 - Z a - z ] [ A - Z a - z 0 - 9 _ \| ] * ) \. ( [ A - Z a - z ] [ A - Z a - z 0 - 9 _ ] * ) \. ( [ A - Z a - z ] [ A - Z a - z 0 - 9 _ ] * ) / g;
35+ private static readonly TERM_REGEX =
36+ / ^ ( \s * | ! | T R U E | F A L S E | \| \| | & & | \( | \) | ( [ A - Z a - z 0 - 9 _ ] + \. [ A - Z a - z 0 - 9 _ ] + (? ! [ A - Z a - z 0 - 9 _ ] * \. [ A - Z a - z 0 - 9 _ ] * ) ) ) + $ / g;
37+
38+ private static readonly LABEL_REGEX = / ( [ A - Z a - z 0 - 9 _ ] + ) \. ( [ A - Z a - z 0 - 9 _ ] + ) / g;
39+
3140 // Regex matching alphanumeric characters.
3241 public static readonly REGEX_ALPHANUMERIC = / [ A - Z a - z 0 - 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