// Copyright 2006-2012 AdvancedTools. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.advancedtools.cpp; import com.advancedtools.cpp.psi.CppElement; import com.advancedtools.cpp.psi.CppKeyword; import com.intellij.extapi.psi.PsiFileBase; import com.intellij.lang.ASTNode; import com.intellij.lang.ParserDefinition; import com.intellij.lang.PsiBuilder; import com.intellij.lang.PsiParser; import com.intellij.lexer.Lexer; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import com.intellij.psi.FileViewProvider; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiFile; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IFileElementType; import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; import java.util.LinkedList; /** * @author maxim * Date: 2/3/12 * Time: 1:24 PM */ public class CppParserDefinition implements ParserDefinition { @NotNull public Lexer createLexer(Project project) { return CppLanguage.createLexerStatic(project); } @NotNull public PsiParser createParser(Project project) { return new CppParser(); } public IFileElementType getFileNodeType() { return CppSupportLoader.CPP_FILE; } @NotNull public TokenSet getWhitespaceTokens() { return CppTokenTypes.WHITE_SPACES; } @NotNull public TokenSet getCommentTokens() { return CppTokenTypes.COMMENTS; } // IDEA8 @NotNull public TokenSet getStringLiteralElements() { return CppTokenTypes.LITERALS; } @NotNull public PsiElement createElement(ASTNode node) { if (CppTokenTypes.KEYWORDS.contains(node.getElementType())) { return new CppKeyword(node); } return new CppElement(node); } public PsiFile createFile(FileViewProvider fileViewProvider) { return new CppFile(fileViewProvider); } public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode astNode, ASTNode astNode1) { return SpaceRequirements.MAY; } static class BlockInfo { static enum BlockType { BLOCK, STATEMENT, FUNC } final PsiBuilder.Marker block; LinkedList<PsiBuilder.Marker> parensList; final BlockType blockType; BlockInfo(PsiBuilder.Marker _block, BlockType _blockType) { block = _block; blockType = _blockType; } void done() { if (parensList != null) { while(parensList.size() > 0) doneParens(); } block.done( blockType == BlockType.BLOCK ? CppTokenTypes.BLOCK: blockType == BlockType.FUNC ? CppTokenTypes.BLOCK: CppTokenTypes.STATEMENT); } public void addParens(PsiBuilder.Marker marker) { if (parensList == null) parensList = new LinkedList<PsiBuilder.Marker>(); parensList.add(marker); } public void doneParens() { if (parensList != null && parensList.size() > 0) parensList.removeLast().done(CppTokenTypes.PARENS); } public boolean hasParens() { return parensList != null && parensList.size() > 0; } } private static class CppParser implements PsiParser { @NotNull public ASTNode parse(IElementType iElementType, PsiBuilder psiBuilder) { final PsiBuilder.Marker rootMarker = psiBuilder.mark(); final LinkedList<BlockInfo> blocks = new LinkedList<BlockInfo>(); boolean openBlock = true; while (psiBuilder.getTokenType() != null) { if (openBlock) { final IElementType type = psiBuilder.getTokenType(); if (type != CppTokenTypes.RBRACE && type != CppTokenTypes.SEMICOLON && type != CppTokenTypes.COMMA && type != CppTokenTypes.LBRACE && (type != CppTokenTypes.PRE_KEYWORD || !psiBuilder.getTokenText().equals("\\"))) { openBlock = false; blocks.add(new BlockInfo(psiBuilder.mark(), blocks.size() > 0 ? BlockInfo.BlockType.STATEMENT : BlockInfo.BlockType.FUNC)); } } final IElementType tokenType = psiBuilder.getTokenType(); if (tokenType == CppTokenTypes.LBRACE) { blocks.add(new BlockInfo(psiBuilder.mark(), BlockInfo.BlockType.BLOCK)); } else if (tokenType == CppTokenTypes.LPAR || tokenType == CppTokenTypes.LBRACKET) { if (blocks.size() > 0) blocks.getLast().addParens(psiBuilder.mark()); } final PsiBuilder.Marker tokenMarker = requiresComposite(tokenType) ? psiBuilder.mark():null; psiBuilder.advanceLexer(); if (tokenMarker != null) tokenMarker.done(tokenType); if (tokenType == CppTokenTypes.LBRACE) { openBlock = true; } else if (tokenType == CppTokenTypes.RPAR || tokenType == CppTokenTypes.RBRACKET) { if (blocks.size() > 0) blocks.getLast().doneParens(); } else if (tokenType == CppTokenTypes.RBRACE && blocks.size() > 0) { blocks.removeLast().done(); if (blocks.size() > 0) blocks.removeLast().done(); openBlock = true; } else if (tokenType == CppTokenTypes.SEMICOLON && blocks.size() > 0 && !blocks.getLast().hasParens() && !openBlock) { blocks.removeLast().done(); openBlock = true; } } while(blocks.size() > 0) { blocks.removeLast().done(); } rootMarker.done(iElementType); return psiBuilder.getTreeBuilt(); } } private static boolean requiresComposite(IElementType tokenType) { return tokenType == CppTokenTypes.IDENTIFIER || tokenType == CppTokenTypes.STRING_LITERAL || CppTokenTypes.OVERRIDABLE_OPERATIONS.contains(tokenType); } public static class CppFile extends PsiFileBase implements com.advancedtools.cpp.psi.CppFile { public CppFile(@NotNull FileViewProvider fileViewProvider) { super(fileViewProvider, CppSupportLoader.CPP_LANGUAGE); } @NotNull public FileType getFileType() { return CppSupportLoader.CPP_FILETYPE; } public void accept(@NotNull PsiElementVisitor psiElementVisitor) { psiElementVisitor.visitFile(this); } @Override public String toString() { return "CppFile:" + getName(); } } }