/* * Copyright 2000-2013 JetBrains s.r.o. * Copyright 2014-2014 AS3Boyan * Copyright 2014-2014 Elias Ku * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.plugins.haxe.lang.lexer; import com.intellij.lexer.Lexer; import com.intellij.lexer.LexerPosition; import com.intellij.lexer.LookAheadLexer; import com.intellij.lexer.MergingLexerAdapter; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.plugins.haxe.config.HaxeProjectSettings; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import gnu.trove.THashSet; import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.Set; import static com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypeSets.*; import static com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes.*; public class HaxeLexer extends LookAheadLexer { public static Key<Object> DEFINES_KEY = Key.create("haxe.test.defines"); private static final TokenSet tokensToMerge = TokenSet.create( MSL_COMMENT, MML_COMMENT, WSNLS ); private final static Set<String> SDK_DEFINES = new THashSet<String>(Arrays.asList( "macro" )); @Nullable private Project myProject; public HaxeLexer(Project project) { super(new MergingLexerAdapter(new HaxeFlexLexer(), tokensToMerge)); myProject = project; } @Override protected void lookAhead(Lexer baseLexer) { if (baseLexer.getTokenType() == PPERROR) { final LexerPosition position = baseLexer.getCurrentPosition(); baseLexer.advance(); while (HaxeTokenTypeSets.WHITESPACES.contains(baseLexer.getTokenType()) || HaxeTokenTypeSets.COMMENTS.contains(baseLexer.getTokenType())) { baseLexer.advance(); } if (HaxeTokenTypeSets.STRINGS.contains(baseLexer.getTokenType())) { while (HaxeTokenTypeSets.STRINGS.contains(baseLexer.getTokenType())) { baseLexer.advance(); } } else { baseLexer.restore(position); } advanceAs(baseLexer, PPERROR); } /*else if (baseLexer.getTokenType() == PPIF || baseLexer.getTokenType() == PPELSEIF) { advanceAs(baseLexer, PPIF); while (!lookAheadExpressionIsTrue(baseLexer)) { IElementType elementType = eatUntil(baseLexer, PPEND, PPELSE, PPELSEIF); if (elementType == PPELSEIF) { advanceAs(baseLexer, PPBODY); continue; } advanceAs(baseLexer, PPBODY); break; } }*/ /*else if (baseLexer.getTokenType() == PPELSE) { eatUntil(baseLexer, PPEND); advanceAs(baseLexer, PPELSE); }*/ //else if (baseLexer.getTokenType() == CONDITIONAL_STATEMENT_ID) { // advanceAs(baseLexer, CONDITIONAL_STATEMENT_ID); //} else { super.lookAhead(baseLexer); } } @Nullable protected static IElementType eatUntil(Lexer baseLexer, IElementType... types) { final Set<IElementType> typeSet = new THashSet<IElementType>(Arrays.asList(types)); IElementType type = null; int counter = 0; do { baseLexer.advance(); type = baseLexer.getTokenType(); /*if (type == PPIF) { ++counter; } if (counter > 0 && type == PPEND) { --counter; baseLexer.advance(); type = baseLexer.getTokenType(); }*/ //if (type == PPIF || type == PPEND) { // baseLexer.advance(); // type = baseLexer.getTokenType(); //} } while (type != null && (!typeSet.contains(type) || counter > 0)); return type; } protected boolean lookAheadExpressionIsTrue(Lexer baseLexer) { IElementType type = null; // reverse polish notation final LinkedList<IElementType> stack = new LinkedList<IElementType>(); final LinkedList<String> rpn = new LinkedList<String>(); final int expressionStartPosition = baseLexer.getTokenStart(); do { final LexerPosition position = baseLexer.getCurrentPosition(); type = baseLexer.getTokenType(); while (HaxeTokenTypeSets.WHITESPACES.contains(type) || HaxeTokenTypeSets.ONLY_COMMENTS.contains(type)) { baseLexer.advance(); type = baseLexer.getTokenType(); } final String tokenText = baseLexer.getTokenText(); if (type == ID) { if (canCalculate(rpn, stack)) { //revert baseLexer.restore(position); break; } rpn.addFirst(tokenText); } else if (type == PRPAREN) { do { IElementType typeOnStack = stack.pollLast(); if (typeOnStack == PLPAREN) { break; } rpn.addFirst(typeOnStack.toString()); } while (!stack.isEmpty()); while (!stack.isEmpty() && stack.getLast() == ONOT) { rpn.addFirst(stack.pollLast().toString()); } } else if (type == OCOND_AND || type == OCOND_OR) { while (!stack.isEmpty() && (stack.getLast() == OCOND_AND || stack.getLast() == OCOND_OR)) { rpn.addFirst(stack.pollLast().toString()); } stack.add(type); } else if (type == ONOT || type == PLPAREN) { stack.add(type); } else { baseLexer.restore(position); break; } } while (advanceAndContinue(baseLexer)); // not empty token if (expressionStartPosition != baseLexer.getTokenStart()) { addToken(PPEXPRESSION); } try { return calculate(rpn, stack); } catch (CalculationException e) { return false; } } private static boolean advanceAndContinue(Lexer baseLexer) { baseLexer.advance(); return true; } private boolean canCalculate(LinkedList<String> rpn, LinkedList<IElementType> stack) { try { calculate(rpn, stack); return true; } catch (CalculationException e) { return false; } } private boolean calculate(LinkedList<String> rpn, LinkedList<IElementType> stack) throws CalculationException { final LinkedList<String> list = new LinkedList<String>(); for (IElementType type : stack) { if (type == PLPAREN) { throw new CalculationException("Balance error"); } list.add(type.toString()); } Collections.reverse(list); list.addAll(rpn); return calculateImpl(list); } private boolean calculateImpl(LinkedList<String> list) throws CalculationException { if (list.isEmpty()) { throw new CalculationException("Incorrect expression"); } final String token = list.pollFirst(); if ("!".equals(token)) { return !calculateImpl(list); } else if ("&&".equals(token)) { boolean a = calculateImpl(list); boolean b = calculateImpl(list); return a && b; } else if ("||".equals(token)) { boolean a = calculateImpl(list); boolean b = calculateImpl(list); return a || b; } else { return isDefined(token); } } protected boolean isDefined(String name) { if (name == null) { return false; } if (myProject == null) { return SDK_DEFINES.contains(name); } String[] definitions = null; if (ApplicationManager.getApplication().isUnitTestMode()) { final Object userData = myProject.getUserData(DEFINES_KEY); if (userData instanceof String) { definitions = ((String)userData).split(","); } } else { definitions = HaxeProjectSettings.getInstance(myProject).getUserCompilerDefinitions(); } return definitions != null && Arrays.asList(definitions).contains(name); } private static class CalculationException extends Exception { private CalculationException(String message) { super(message); } } }