@@ -20,22 +20,42 @@ interface PortBehaviorValidationError {
2020 */
2121@injectable ( )
2222export class PortBehaviorValidator {
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 _ ] + ) ) * ) ? ) \} \) + $ / ;
23+ // RegEx validating names of input pins
24+ private static readonly INPUT_LABEL_REGEX = / [ A - Z a - z 0 - 9 _ ~ ] [ A - Z a - z 0 - 9 _ ~ \| ] + / ;
2725
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 _ \| ] + ) * \} \) $ / ;
26+ // RegEx validating names of output labels
27+ private static readonly OUTPUT_LABEL_REGEX = / [ A - Z a - z 0 - 9 _ ] + \. [ A - Z a - z 0 - 9 _ ] + / ;
3228
3329 // Regex that validates a term
3430 // Has the label type and label value that should be set as capturing groups.
3531 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;
32+ / (?: \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;
33+
34+ // Regex that validates assignments
35+ // Matches "assign out_labels if term from in_pins" where out_labels is a comma separated list of output labels, in_pins is a comma separated list of input pins and the from part is optional.
36+ private static readonly ASSIGNMENT_REGEX = new RegExp (
37+ "^assign (" +
38+ PortBehaviorValidator . BUILD_COMMA_SEPARATED_LIST_REGEX ( PortBehaviorValidator . OUTPUT_LABEL_REGEX ) . source +
39+ ") if (" +
40+ PortBehaviorValidator . TERM_REGEX . source +
41+ ")(?: from (" +
42+ PortBehaviorValidator . BUILD_COMMA_SEPARATED_LIST_REGEX ( PortBehaviorValidator . INPUT_LABEL_REGEX ) . source +
43+ "))?$" ,
44+ ) ;
3745
38- private static readonly LABEL_REGEX = / ( [ A - Z a - z 0 - 9 _ ] + ) \. ( [ A - Z a - z 0 - 9 _ ] + ) / g;
46+ // Regex that validates forwarding
47+ // Matches "forward input_pins" where input_pins is a comma separated list of input pins.
48+ private static readonly FORWARDING_REGEX = new RegExp (
49+ "^forward " +
50+ PortBehaviorValidator . BUILD_COMMA_SEPARATED_LIST_REGEX ( PortBehaviorValidator . INPUT_LABEL_REGEX ) . source +
51+ "$" ,
52+ ) ;
53+
54+ private static readonly SET_AND_UNSET_REGEX = new RegExp (
55+ "^(un)?set " +
56+ PortBehaviorValidator . BUILD_COMMA_SEPARATED_LIST_REGEX ( PortBehaviorValidator . OUTPUT_LABEL_REGEX ) . source +
57+ "$" ,
58+ ) ;
3959
4060 // Regex matching alphanumeric characters.
4161 public static readonly REGEX_ALPHANUMERIC = / [ A - Z a - z 0 - 9 _ \| ] + / ;
@@ -83,12 +103,16 @@ export class PortBehaviorValidator {
83103 return ;
84104 }
85105
86- if ( line . startsWith ( "Forwarding " ) ) {
106+ if ( line . startsWith ( "forward " ) ) {
87107 return this . validateForwardStatement ( line , lineNumber , port ) ;
88108 }
89109
90- if ( line . startsWith ( "Assignment" ) ) {
91- return this . validateSetStatement ( line , lineNumber , port ) ;
110+ if ( line . startsWith ( "set" ) || line . startsWith ( "unset" ) ) {
111+ return this . validateSetAndUnsetStatement ( line , lineNumber ) ;
112+ }
113+
114+ if ( line . startsWith ( "assign" ) ) {
115+ return this . validateAssignStatement ( line , lineNumber , port ) ;
92116 }
93117
94118 return [
@@ -109,12 +133,12 @@ export class PortBehaviorValidator {
109133 return [
110134 {
111135 line : lineNumber ,
112- message : "invalid forwarding(Template:Forwarding({in_ports} )" ,
136+ message : "invalid forwarding(Template: forward <i>input_pins</i> )" ,
113137 } ,
114138 ] ;
115139 }
116140
117- const inputsString = line . substring ( "Forwarding({ " . length , line . length - 2 ) ;
141+ const inputsString = line . substring ( "forward " . length ) ;
118142 const inputs = inputsString . split ( "," ) . map ( ( input ) => input . trim ( ) ) ;
119143 if ( inputs . filter ( ( input ) => input !== "" ) . length === 0 ) {
120144 return [
@@ -211,68 +235,137 @@ export class PortBehaviorValidator {
211235 return undefined ;
212236 }
213237
214- private validateSetStatement (
215- line : string ,
216- lineNumber : number ,
217- port : DfdOutputPortImpl ,
218- ) : PortBehaviorValidationError [ ] | undefined {
219- const match = line . match ( PortBehaviorValidator . ASSIGNMENT_REGEX ) ;
238+ private validateSetAndUnsetStatement ( line : string , lineNumber : number ) : PortBehaviorValidationError [ ] | undefined {
239+ const match = line . match ( PortBehaviorValidator . SET_AND_UNSET_REGEX ) ;
220240 if ( ! match ) {
221241 return [
222242 {
223243 line : lineNumber ,
224- message : "invalid assignment(Template:Assignment({in_ports}; term; {out_label})" ,
244+ message :
245+ "invalid assignment(Template:" +
246+ ( line . startsWith ( "set" ) ? "set" : "unset" ) +
247+ " <i>out_labels</i>)" ,
225248 } ,
226249 ] ;
227250 }
228251
229- // Parenthesis must be balanced.
230- let parenthesisLevel = 0 ;
231- for ( let strIdx = 0 ; strIdx < line . length ; strIdx ++ ) {
232- const char = line [ strIdx ] ;
233- if ( char === "(" ) {
234- parenthesisLevel ++ ;
235- } else if ( char === ")" ) {
236- parenthesisLevel -- ;
252+ const inputAccessErrors = [ ] ;
253+
254+ const outLabel = line
255+ . substring ( ( line . startsWith ( "set" ) ? "set" : "unset" ) . length + 1 )
256+ . trim ( )
257+ . split ( "," )
258+ . map ( ( variable ) => variable . trim ( ) ) ;
259+
260+ for ( const typeValuePair of outLabel ) {
261+ if ( typeValuePair === "" ) continue ;
262+
263+ const inputLabelType = typeValuePair . split ( "." ) [ 0 ] . trim ( ) ;
264+ const inputLabelTypeObject = this . labelTypeRegistry
265+ ?. getLabelTypes ( )
266+ . find ( ( type ) => type . name === inputLabelType ) ;
267+ if ( ! inputLabelTypeObject ) {
268+ let idx = line . indexOf ( inputLabelType ) ;
269+ while ( idx !== - 1 ) {
270+ // Check that this is not a substring of another label type.
271+ if (
272+ // must start after a dot and end before a dot
273+ line [ idx - 1 ] === "." &&
274+ line [ idx + inputLabelType . length ] === "."
275+ ) {
276+ inputAccessErrors . push ( {
277+ line : lineNumber ,
278+ message : `unknown label type: ${ inputLabelType } ` ,
279+ colStart : idx ,
280+ colEnd : idx + inputLabelType . length ,
281+ } ) ;
282+ }
283+
284+ idx = line . indexOf ( inputLabelType , idx + 1 ) ;
285+ }
286+ }
287+
288+ if ( typeValuePair . indexOf ( "." ) !== - 1 ) {
289+ if ( typeValuePair . split ( "." ) [ 1 ] === null || typeValuePair . split ( "." ) [ 1 ] === "" ) continue ;
290+ const inputLabelValue = typeValuePair . split ( "." ) [ 1 ] . trim ( ) ;
291+
292+ const inputLabelTypeObject = this . labelTypeRegistry
293+ ?. getLabelTypes ( )
294+ . find ( ( type ) => type . name === inputLabelType ) ;
295+ if ( ! inputLabelTypeObject ) {
296+ let idx = line . indexOf ( inputLabelType ) ;
297+ while ( idx !== - 1 ) {
298+ // Check that this is not a substring of another label type.
299+ if (
300+ // must start after a dot and end before a dot
301+ line [ idx - 1 ] === "." &&
302+ line [ idx + inputLabelType . length ] === "."
303+ ) {
304+ inputAccessErrors . push ( {
305+ line : lineNumber ,
306+ message : `unknown label type: ${ inputLabelType } ` ,
307+ colStart : idx ,
308+ colEnd : idx + inputLabelType . length ,
309+ } ) ;
310+ }
311+
312+ idx = line . indexOf ( inputLabelType , idx + 1 ) ;
313+ }
314+ } else if ( ! inputLabelTypeObject . values . find ( ( value ) => value . text === inputLabelValue ) ) {
315+ let idx = line . indexOf ( inputLabelValue ) ;
316+ while ( idx !== - 1 ) {
317+ // Check that this is not a substring of another label value.
318+ if (
319+ // must start after a dot and end at the end of the alphanumeric text
320+ line [ idx - 1 ] === "." &&
321+ // Might be at the end of the line
322+ ( ! line [ idx + inputLabelValue . length ] ||
323+ ! line [ idx + inputLabelValue . length ] . match ( PortBehaviorValidator . REGEX_ALPHANUMERIC ) )
324+ ) {
325+ inputAccessErrors . push ( {
326+ line : lineNumber ,
327+ message : `unknown label value of label type ${ inputLabelType } : ${ inputLabelValue } ` ,
328+ colStart : idx ,
329+ colEnd : idx + inputLabelValue . length ,
330+ } ) ;
331+ }
332+
333+ idx = line . indexOf ( inputLabelValue , idx + 1 ) ;
334+ }
335+ }
237336 }
238337
239- if ( parenthesisLevel < 0 ) {
240- return [
241- {
242- line : lineNumber ,
243- message : "invalid assignment: missing opening parenthesis" ,
244- colStart : strIdx ,
245- colEnd : strIdx + 1 ,
246- } ,
247- ] ;
338+ if ( typeValuePair . split ( "." ) [ 2 ] !== undefined ) {
339+ inputAccessErrors . push ( {
340+ line : lineNumber ,
341+ message : `invalid label definition` ,
342+ } ) ;
248343 }
249344 }
250345
251- if ( parenthesisLevel !== 0 ) {
346+ return inputAccessErrors . length > 0 ? inputAccessErrors : [ ] ;
347+ }
348+
349+ private validateAssignStatement (
350+ line : string ,
351+ lineNumber : number ,
352+ port : DfdOutputPortImpl ,
353+ ) : PortBehaviorValidationError [ ] | undefined {
354+ const match = line . match ( PortBehaviorValidator . ASSIGNMENT_REGEX ) ;
355+ if ( ! match ) {
252356 return [
253357 {
254358 line : lineNumber ,
255- message : "invalid assignment: missing closing parenthesis " ,
359+ message : "invalid assignment(Template:assign out_labels if term from in_pins) " ,
256360 } ,
257361 ] ;
258362 }
259363
260364 // Extract all used inputs, label types and the corresponding label values.
261- var term = line . split ( ";" ) [ 1 ] . trim ( ) ; // get everything after the ;
262- if ( term . length === 0 ) {
263- return [
264- {
265- line : lineNumber ,
266- message : "invalid assignment: missing term" ,
267- } ,
268- ] ;
269- }
270- if ( term . indexOf ( ";" ) !== - 1 ) {
271- term = term . split ( ";" ) [ 0 ] ;
272- }
365+ let term = match [ 2 ] ;
273366
274367 const termMatch = term . match ( PortBehaviorValidator . TERM_REGEX ) ;
275- if ( ! termMatch ) {
368+ if ( term == "" || ! termMatch ) {
276369 return [
277370 {
278371 line : lineNumber ,
@@ -281,9 +374,13 @@ export class PortBehaviorValidator {
281374 ] ;
282375 }
283376
284- const matches = [ ...term . matchAll ( PortBehaviorValidator . LABEL_REGEX ) ] ;
377+ const matches = [
378+ ...term . matchAll ( new RegExp ( "(" + PortBehaviorValidator . OUTPUT_LABEL_REGEX . source + ")" , "g" ) ) ,
379+ ] ;
285380 const inputAccessErrors = [ ] ;
286381
382+ console . log ( matches ) ;
383+
287384 for ( const inputMatch of matches ) {
288385 const inputLabelType = inputMatch [ 1 ] ;
289386 const inputLabelValue = inputMatch [ 2 ] ;
@@ -353,19 +450,8 @@ export class PortBehaviorValidator {
353450 }
354451 const availableInputs = node . getAvailableInputs ( ) ;
355452
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 ( ) ) ;
453+ const outLabel = match [ 1 ] . split ( "," ) . map ( ( variable ) => variable . trim ( ) ) ;
454+ const inPorts = match [ 3 ] ? match [ 3 ] . split ( "," ) . map ( ( variable ) => variable . trim ( ) ) : [ ] ;
369455
370456 // Check for each input access that the input exists and that the label type and value are valid.
371457
@@ -472,4 +558,8 @@ export class PortBehaviorValidator {
472558
473559 return inputAccessErrors . length > 0 ? inputAccessErrors : [ ] ;
474560 }
561+
562+ private static BUILD_COMMA_SEPARATED_LIST_REGEX ( regex : RegExp ) : RegExp {
563+ return new RegExp ( regex . source + "(?:, *" + regex . source + ")*" ) ;
564+ }
475565}
0 commit comments