001/*-------------------------------------------------------------------------+ 002| | 003| Copyright (c) 2009-2020 CQSE GmbH | 004| | 005+-------------------------------------------------------------------------*/ 006package eu.cqse.check.framework.shallowparser.languages.go; 007 008import static eu.cqse.check.framework.scanner.ETokenType.ADD; 009import static eu.cqse.check.framework.scanner.ETokenType.BACKSLASH; 010import static eu.cqse.check.framework.scanner.ETokenType.COLON; 011import static eu.cqse.check.framework.scanner.ETokenType.DOT; 012import static eu.cqse.check.framework.scanner.ETokenType.LBRACE; 013import static eu.cqse.check.framework.scanner.ETokenType.LBRACK; 014import static eu.cqse.check.framework.scanner.ETokenType.LPAREN; 015import static eu.cqse.check.framework.scanner.ETokenType.MINUSMINUS; 016import static eu.cqse.check.framework.scanner.ETokenType.NOT; 017import static eu.cqse.check.framework.scanner.ETokenType.PLUSPLUS; 018import static eu.cqse.check.framework.scanner.ETokenType.QUESTION; 019import static eu.cqse.check.framework.scanner.ETokenType.RBRACE; 020import static eu.cqse.check.framework.scanner.ETokenType.RBRACK; 021import static eu.cqse.check.framework.scanner.ETokenType.RPAREN; 022import static eu.cqse.check.framework.scanner.ETokenType.SEMICOLON; 023 024import java.util.EnumMap; 025import java.util.EnumSet; 026import java.util.List; 027import java.util.Map; 028import java.util.Stack; 029 030import eu.cqse.check.framework.scanner.ETokenType; 031import eu.cqse.check.framework.scanner.IToken; 032import eu.cqse.check.framework.shallowparser.TokenStreamUtils; 033import eu.cqse.check.framework.shallowparser.framework.ParserState; 034import eu.cqse.check.framework.shallowparser.framework.RecognizerBase; 035import eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates; 036 037/** 038 * Recognizer for the end of statements in Go. We need a separate recognizer as 039 * the rules for statement continuation are non-trivial due to the optional 040 * semicolon. 041 */ 042public class GoSkipToEndOfStatementRecognizer extends RecognizerBase<EGenericParserStates> { 043 044 /** Token types that force a statement to continue even in a new line. */ 045 private static final EnumSet<ETokenType> CONTINUE_TOKENS = EnumSet.of(BACKSLASH, DOT, QUESTION, COLON, NOT, ADD); 046 047 /** Token types of class OPERATOR that do not force a statement continuation. */ 048 private static final EnumSet<ETokenType> NON_CONTINUATION_OPERATORS = EnumSet.of(MINUSMINUS, PLUSPLUS); 049 050 /** Matched tokens for nesting in complex Xtend statements. */ 051 private static final Map<ETokenType, ETokenType> NESTING_MATCH = new EnumMap<>(ETokenType.class); 052 053 static { 054 NESTING_MATCH.put(LPAREN, RPAREN); 055 NESTING_MATCH.put(LBRACK, RBRACK); 056 NESTING_MATCH.put(LBRACE, RBRACE); 057 } 058 059 /** 060 * Defines if we should include the lastToken into the matching decision from 061 * the start on. 062 */ 063 private boolean forceMatch = false; 064 065 /** @see #forceMatch */ 066 public void setForceMatch(boolean forceMatch) { 067 this.forceMatch = forceMatch; 068 } 069 070 /** {@inheritDoc} */ 071 @Override 072 protected int matchesLocally(ParserState<EGenericParserStates> parserState, List<IToken> tokens, int startOffset) { 073 074 if (startOffset < 0) { 075 return NO_MATCH; 076 } 077 IToken lastToken = null; 078 if (!forceMatch && startOffset > 0) { 079 lastToken = tokens.get(startOffset - 1); 080 } 081 082 Stack<ETokenType> expectedClosing = new Stack<>(); 083 084 while (true) { 085 if (startOffset >= tokens.size()) { 086 return startOffset; 087 } 088 089 IToken token = tokens.get(startOffset); 090 ETokenType tokenType = token.getType(); 091 092 if (!expectedClosing.isEmpty() && tokenType == expectedClosing.peek()) { 093 expectedClosing.pop(); 094 } else if (expectedClosing.isEmpty() && tokenType == SEMICOLON 095 && tokens.get(0).getType() != ETokenType.FOR) { 096 return startOffset + 1; 097 } else if (expectedClosing.isEmpty() && TokenStreamUtils.startsNewStatement(token, lastToken, 098 CONTINUE_TOKENS, NON_CONTINUATION_OPERATORS, RBRACE)) { 099 return startOffset; 100 } else if (NESTING_MATCH.containsKey(tokenType)) { 101 expectedClosing.push(NESTING_MATCH.get(tokenType)); 102 } else { 103 startOffset++; 104 lastToken = tokens.get(startOffset - 1); 105 continue; 106 } 107 108 lastToken = token; 109 startOffset += 1; 110 } 111 } 112}