/******************************************************************************
 * Copyright 2021 TypeFox GmbH
 * This program and the accompanying materials are made available under the
 * terms of the MIT License, which is available in the project root.
 ******************************************************************************/ import * as ast from '../../grammar/generated/ast' ; import { Cardinality , isArray , isOptional } from '../../grammar/grammar-util' ; param featureStack returns export function findNextFeatures ( featureStack ) { return findNextFeaturesInternal ( featureStack , new Map < ast . AbstractElement , Cardinality > ( ) ) ; } export function findNextFeaturesInternal ( featureStack , cardinalities ) { if ( featureStack . length === 0 ) { return [ ] ; } const features = [ ] ; const feature = featureStack [ 0 ] ; let parent ; let item = feature ; while ( item . $container ) { if ( ast . isGroup ( item . $container ) ) { parent = item . $container ; break ; } else if ( ast . isAbstractElement ( item . $container ) ) { item = item . $container ; } else { break ; } } if ( isArray ( item . cardinality ?? cardinalities . get ( item ) ) ) { features . push ( ... findFirstFeatures ( item , cardinalities ) ) ; } if ( parent ) { const ownIndex = parent . elements . indexOf ( item ) ; if ( ownIndex !== undefined && ownIndex < parent . elements . length - 1 ) { features . push ( ... findNextFeaturesInGroup ( parent , ownIndex + 1 , cardinalities ) ) ; } if ( features . every ( e => isOptional ( e . cardinality ?? cardinalities . get ( e ) ) ) ) { features . push ( ... findNextFeaturesInternal ( [ parent ] , cardinalities ) ) ; } if ( features . every ( e => isOptional ( e . cardinality ?? cardinalities . get ( e ) ) ) ) { featureStack . shift ( ) ; features . push ( ... findNextFeaturesInternal ( featureStack , cardinalities ) ) ; } } else { featureStack . shift ( ) ; features . push ( ... findNextFeaturesInternal ( featureStack , cardinalities ) ) ; } return features ; } param feature returns export function findFirstFeatures ( feature , cardinalities ? ) { const card = cardinalities ?? new Map ( ) ; if ( feature === undefined ) { return [ ] ; } else if ( ast . isGroup ( feature ) ) { return findNextFeaturesInGroup ( feature , 0 , card ) . map ( e => modifyCardinality ( e , feature . cardinality , card ) ) ; } else if ( ast . isAlternatives ( feature ) ) { return feature . elements . flatMap ( e => findFirstFeatures ( e , card ) ) . map ( e => modifyCardinality ( e , feature . cardinality , card ) ) ; } else if ( ast . isUnorderedGroup ( feature ) ) { return [ ] ; } else if ( ast . isAssignment ( feature ) ) { return findFirstFeatures ( feature . terminal , card ) . map ( e => modifyCardinality ( e , feature . cardinality , card ) ) ; } else if ( ast . isAction ( feature ) ) { return findNextFeaturesInternal ( [ feature ] , card ) . map ( e => modifyCardinality ( e , feature . cardinality , card ) ) ; } else if ( ast . isRuleCall ( feature ) && ast . isParserRule ( feature . rule . ref ) ) { return findFirstFeatures ( feature . rule . ref . alternatives , card ) . map ( e => modifyCardinality ( e , feature . cardinality , card ) ) ; } else { return [ feature ] ; } } param feature param cardinality returns function modifyCardinality ( feature , cardinality , cardinalities ) { if ( isOptional ( cardinality ) ) { if ( isArray ( feature . cardinality ) ) { cardinalities . set ( feature , '*' ) ; } else { cardinalities . set ( feature , '?' ) ; } } return feature ; } function findNextFeaturesInGroup ( group , index , cardinalities ) { const features = [ ] ; let firstFeature ; do { firstFeature = group . elements [ index ++ ] ; features . push ( ... findFirstFeatures ( firstFeature , cardinalities ) ) ; if ( ! isOptional ( firstFeature ?. cardinality ?? cardinalities . get ( firstFeature ) ) ) { break ; } } while ( firstFeature ) ; return features ; }