@@ -224,11 +224,7 @@ def find_character_literal_candidates(lQuotes, lChars):
224224
225225def is_character_literal_candidate (iIndex , lQuotes , lChars ):
226226 iQuote = lQuotes [iIndex ]
227- return (
228- there_is_a_single_token_between_quotes (iIndex , lQuotes )
229- and token_between_quotes_is_a_single_character (iQuote , lChars )
230- and token_is_not_a_parenthesis (iQuote , lChars )
231- )
227+ return there_is_a_single_token_between_quotes (iIndex , lQuotes ) and token_between_quotes_is_a_single_character (iQuote , lChars )
232228
233229
234230def there_is_a_single_token_between_quotes (iIndex , lQuotes ):
@@ -239,19 +235,40 @@ def token_between_quotes_is_a_single_character(iQuote, lChars):
239235 return len (lChars [iQuote + 1 ]) == 1
240236
241237
242- def token_is_not_a_parenthesis (iQuote , lChars ):
243- return lChars [iQuote + 1 ] != "("
244-
245-
246- def filter_character_literal_candidates (lLiterals ):
238+ def filter_character_literal_candidates (lCandidates ):
247239 lReturn = []
248- for iIndex , lLiteral in enumerate (lLiterals [0 :- 1 ]):
249- lNextLiteral = lLiterals [iIndex + 1 ]
250- lPreviousLiteral = lLiterals [iIndex - 1 ]
251- if lLiteral [1 ] == lNextLiteral [0 ] and lLiteral [0 ] == lPreviousLiteral [1 ]:
252- continue
253- lReturn .append (lLiteral )
254- lReturn .append (lLiterals [- 1 ])
240+ lSequentialCandidates = []
241+ for iIndex , lCandidate in enumerate (lCandidates ):
242+ # The algorithm is a bit more complex than one might expect because it needs to be able to handle sequences of
243+ # character literals separated by a single character, e.g. `'1','0','a'`, as well as character literals inside
244+ # qualified expressions, e.g. std_logic'('1'), both of which include "red herring" candidates.
245+ # First, build up a sequence of sequential candidates, i.e. candidates that are separated by one character. Most
246+ # of the time, this sequence will be one long.
247+ lSequentialCandidates .append (lCandidate )
248+
249+ bCandidateIsLast = iIndex == len (lCandidates ) - 1
250+ if bCandidateIsLast :
251+ bCandidateIsLastInSequence = True
252+ else :
253+ lNextLiteral = lCandidates [iIndex + 1 ]
254+ bCandidateIsLastInSequence = lCandidate [1 ] != lNextLiteral [0 ]
255+
256+ if bCandidateIsLastInSequence :
257+ # At the end of a sequence, filter the candidates to find the character literals. Sequential candidates will
258+ # alternate between valid and invalid candidates. For example, in `'1','0'`, the first candidate ('1') is
259+ # valid, the second (',') is invalid, and the third ('0') is valid. The first in the sequence will always be
260+ # valid unless a qualified expression is present. For example, in `std_logic'('1')`, the first candidate
261+ # ('(') is invalid and the second candidate ('1') is valid. If there is a qualified expression, the number
262+ # of candidates will be even; otherwise the number will be odd.
263+ # Therefore, filter by selecting every second candidate, starting with 0 if the number of candidates is odd
264+ # and starting with 1 if the number of candidates is even.
265+ iSequenceStart = (len (lSequentialCandidates ) + 1 ) % 2
266+ lFilteredLiterals = [lSequentialCandidates [x ] for x in range (iSequenceStart , len (lSequentialCandidates ), 2 )]
267+ lReturn .extend (lFilteredLiterals )
268+
269+ # Clear the sequential candidates for the next sequence.
270+ lSequentialCandidates = []
271+
255272 return lReturn
256273
257274
0 commit comments