@@ -75,8 +75,8 @@ function evaluateConformanceExpression(expression, elementMap) {
7575 // if any operand is desc, the conformance is too complex to parse
7676 for ( let part of parts ) {
7777 let operands = getOperandsFromExpression ( part )
78- if ( operands && operands . includes ( dbEnum . conformanceTag . desc ) ) {
79- return dbEnum . conformanceTag . desc
78+ if ( operands && operands . includes ( dbEnum . conformanceTag . described ) ) {
79+ return dbEnum . conformanceTag . described
8080 }
8181 }
8282 for ( let part of parts ) {
@@ -169,7 +169,7 @@ function filterRelatedDescElements(elements, featureCode) {
169169 let operands = getOperandsFromExpression ( element . conformance )
170170 return (
171171 operands &&
172- operands . includes ( dbEnum . conformanceTag . desc ) &&
172+ operands . includes ( dbEnum . conformanceTag . described ) &&
173173 operands . includes ( featureCode )
174174 )
175175 } )
@@ -238,9 +238,118 @@ function checkFeaturesToUpdate(
238238 return { updatedFeatures, changedConformFeatures }
239239}
240240
241+ /**
242+ * Translate a conformance tag to its corresponding value.
243+ *
244+ * @param {string } expression
245+ * @returns {string } The translated conformance value.
246+ */
247+ function translateConformanceTag ( expression ) {
248+ let tagKeys = Object . keys ( dbEnum . conformanceTag )
249+ for ( let key of tagKeys ) {
250+ if ( expression === dbEnum . conformanceTag [ key ] ) {
251+ return dbEnum . conformanceVal [ key ]
252+ }
253+ }
254+ return ''
255+ }
256+
257+ /**
258+ * Translate a boolean expression into natural language.
259+ *
260+ * @param {string } expr
261+ * @returns {string } The translated boolean expression.
262+ */
263+ function translateBooleanExpr ( expr ) {
264+ // match operands and operators
265+ let tokens = expr . match ( / [ A - Z a - z 0 - 9 _ ] + | [ ! & | ( ) ] / g) || [ ]
266+ let output = [ ]
267+
268+ for ( let i = 0 ; i < tokens . length ; i ++ ) {
269+ let token = tokens [ i ]
270+
271+ if ( token === '&' ) {
272+ output . push ( dbEnum . logicalOperators . and )
273+ } else if ( token === '|' ) {
274+ output . push ( dbEnum . logicalOperators . or )
275+ } else if ( token === '(' || token === ')' ) {
276+ output . push ( token )
277+ } else if ( token === '!' ) {
278+ // For the '!' operator: if it precedes '()', translate as 'not';
279+ // otherwise, combine with the next operand as '<operand> is not enabled'
280+ let next = tokens [ i + 1 ]
281+ if ( next === '(' ) {
282+ output . push ( dbEnum . logicalOperators . not )
283+ } else {
284+ output . push ( `${ next } is not enabled` )
285+ i ++ // Skip the next token since we consumed it
286+ }
287+ } else {
288+ // if none of the above is matched, it is an element operand
289+ output . push ( `${ token } is enabled` )
290+ }
291+ }
292+
293+ return output . join ( ' ' )
294+ }
295+
296+ /**
297+ * Translate a conformance expression into natural language.
298+ *
299+ * @export
300+ * @param {string } expression
301+ * @returns {string } The translated conformance expression.
302+ */
303+ function translateConformanceExpression ( expression ) {
304+ if ( ! expression ) return ''
305+
306+ let conformanceTag = translateConformanceTag ( expression )
307+ if ( conformanceTag ) return conformanceTag
308+
309+ // special case on provisional conformance in format of "P, <expression>"
310+ // handle 'P,' separately and use recursion to translate the rest
311+ if ( expression . startsWith ( dbEnum . conformanceTag . provisional + ',' ) ) {
312+ let rest = expression . slice ( 2 ) . trim ( )
313+ let translatedRest = translateConformanceExpression ( rest )
314+ return `provisional for now. When not provisional in the future, it is ${ translatedRest } `
315+ }
316+
317+ // split by ',' to handle each expression in otherwise conformance separately
318+ let parts = expression . split ( ',' ) . map ( ( p ) => p . trim ( ) )
319+ let translatedParts = parts . map ( ( part ) => {
320+ let conformanceTag = translateConformanceTag ( part )
321+ if ( conformanceTag ) return conformanceTag
322+
323+ // handle optional expressions surrounded by '[]'
324+ let optionalMatch = part . match ( / ^ \[ ( .* ) \] $ / )
325+ if ( optionalMatch ) {
326+ // optionalMatch[1] is the expression inside '[]'
327+ let optionalText = translateBooleanExpr ( optionalMatch [ 1 ] )
328+ return `${ dbEnum . conformanceVal . optional } if ${ optionalText } `
329+ }
330+
331+ // otherwise it's a regular mandatory expression
332+ let translated = translateBooleanExpr ( part )
333+ return `${ dbEnum . conformanceVal . mandatory } if ${ translated } `
334+ } )
335+
336+ // join translated parts with 'otherwise'
337+ let result = ''
338+ for ( let i = 0 ; i < translatedParts . length ; i ++ ) {
339+ let prefix = i === 0 ? '' : ', otherwise it is '
340+ result += `${ prefix } ${ translatedParts [ i ] } `
341+ }
342+ // if the last part is not a conformance tag, fall back to not supported
343+ if ( ! translateConformanceTag ( parts [ parts . length - 1 ] ) ) {
344+ result += ', otherwise it is not supported'
345+ }
346+ return result
347+ }
348+
241349exports . evaluateConformanceExpression = evaluateConformanceExpression
242350exports . checkMissingOperands = checkMissingOperands
243351exports . checkIfExpressionHasOperand = checkIfExpressionHasOperand
244352exports . checkFeaturesToUpdate = checkFeaturesToUpdate
245353exports . filterRelatedDescElements = filterRelatedDescElements
246354exports . getOperandsFromExpression = getOperandsFromExpression
355+ exports . translateConformanceExpression = translateConformanceExpression
0 commit comments