package com.haskforce.psi; import com.haskforce.HaskellLanguage; import com.haskforce.parser.HaskellParser; import com.haskforce.parsing._HaskellParsingLexer; import com.intellij.lang.ASTNode; import com.intellij.lang.ITokenTypeRemapper; import com.intellij.lang.PsiBuilder; import com.intellij.lang.impl.PsiBuilderImpl; import com.intellij.lexer.FlexAdapter; import com.intellij.openapi.util.Pair; import com.intellij.psi.TokenType; import com.intellij.psi.tree.IElementType; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import java.util.HashMap; /** * Wraps the entry-point for the Grammar-Kit parser to register * a token-remapper. */ public class HaskellParserWrapper extends HaskellParser { private static final Pair<Integer, Integer> INIT_KEY = Pair.create(0, 0); public int rbraceDebt; public int maxRbraceDebt; public int lastCountedTok; public boolean regressed; public _HaskellParsingLexer lexer; public final HashMap<Integer, Pair<Integer,Integer>> debtPoints = ContainerUtil.newHashMap(); final ITokenTypeRemapper myRemapper = new ITokenTypeRemapper() { /** * Intercept synthetic rbraces and varsymplus tokens and correct them. */ @Override public IElementType filter(IElementType source, int start, int end, CharSequence text) { // int lastLastCountedTok = lastCountedTok; if (start > lastCountedTok) { lastCountedTok = start; regressed = false; } else { regressed = start != lastCountedTok; } if (HaskellTypes.WHITESPACERBRACETOK.equals(source)) { if (rbraceDebt > 0) { rbraceDebt--; return TokenType.WHITE_SPACE; } return source; } if (!HaskellTypes.VARSYMTOKPLUS.equals(source)) return source; String token = text.toString(); if (HaskellLanguage.RESERVEDOPS.contains(token)) { // Lexer somehow missed lexing the op if we end up here. throw new RuntimeException("Internal Error: Unexpected reservedop: " + token); } if ("--".equals(token) || "---".equals(token)) { return HaskellTypes.DASHES; } return HaskellTypes.VARSYMTOK; } }; @NotNull @Override public ASTNode parse(IElementType root_, PsiBuilder builder_) { maxRbraceDebt = -1; lastCountedTok = -1; regressed = false; debtPoints.clear(); builder_.setTokenTypeRemapper(myRemapper); lexer = (_HaskellParsingLexer) ((FlexAdapter) ((PsiBuilderImpl) builder_).getLexer()).getFlex(); ASTNode node = super.parse(root_, builder_); return node; } /** * Increases how many synthetic rbraces the remapper should consume. */ public boolean increaseRbraceDebt(int offset) { if (maxRbraceDebt < 1) return false; Pair<Integer,Integer> oldValue = ContainerUtil.getOrCreate(debtPoints, offset, INIT_KEY); Pair<Integer,Integer> newValue; Integer snd = oldValue.getSecond(); if (oldValue.getFirst() == 0) { newValue = Pair.create(maxRbraceDebt, maxRbraceDebt - 1); } else { newValue = Pair.create(oldValue.getFirst(), --snd); } rbraceDebt++; maxRbraceDebt--; debtPoints.put(offset, newValue); return true; } }