| /* |
| * Copyright 2000-2012 JetBrains s.r.o. |
| * |
| * 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.lang.pratt; |
| |
| import com.intellij.lang.ITokenTypeRemapper; |
| import com.intellij.lang.LangBundle; |
| import com.intellij.lang.PsiBuilder; |
| import com.intellij.lang.impl.PsiBuilderImpl; |
| import com.intellij.lexer.Lexer; |
| import com.intellij.openapi.util.Trinity; |
| import com.intellij.psi.tree.IElementType; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| /** |
| * @author peter |
| */ |
| public class PrattBuilderImpl extends PrattBuilder { |
| private final PsiBuilder myBuilder; |
| private final PrattBuilder myParentBuilder; |
| private final PrattRegistry myRegistry; |
| private final LinkedList<IElementType> myLeftSiblings = new LinkedList<IElementType>(); |
| private boolean myParsingStarted; |
| private String myExpectedMessage; |
| private int myPriority = Integer.MIN_VALUE; |
| private MutableMarker myStartMarker; |
| |
| private PrattBuilderImpl(final PsiBuilder builder, final PrattBuilder parent, final PrattRegistry registry) { |
| myBuilder = builder; |
| myParentBuilder = parent; |
| myRegistry = registry; |
| } |
| |
| public static PrattBuilder createBuilder(final PsiBuilder builder, final PrattRegistry registry) { |
| return new PrattBuilderImpl(builder, null, registry); |
| } |
| |
| @Override |
| public PrattBuilder expecting(final String expectedMessage) { |
| myExpectedMessage = expectedMessage; |
| return this; |
| } |
| |
| @Override |
| public PrattBuilder withLowestPriority(final int priority) { |
| myPriority = priority; |
| return this; |
| } |
| |
| @Override |
| public Lexer getLexer() { |
| return ((PsiBuilderImpl) myBuilder).getLexer(); |
| } |
| |
| @Override |
| public void setTokenTypeRemapper(@Nullable final ITokenTypeRemapper remapper) { |
| myBuilder.setTokenTypeRemapper(remapper); |
| } |
| |
| @Override |
| public MutableMarker mark() { |
| return new MutableMarker(myLeftSiblings, myBuilder.mark(), myLeftSiblings.size()); |
| } |
| |
| @Override |
| @Nullable |
| public IElementType parse() { |
| checkParsed(); |
| return myLeftSiblings.size() != 1 ? null : myLeftSiblings.getLast(); |
| } |
| |
| @Override |
| protected PrattBuilder createChildBuilder() { |
| assert myParsingStarted; |
| return new PrattBuilderImpl(myBuilder, this, myRegistry) { |
| @Override |
| protected void doParse() { |
| super.doParse(); |
| PrattBuilderImpl.this.myLeftSiblings.addAll(getResultTypes()); |
| } |
| }; |
| } |
| |
| protected void doParse() { |
| if (isEof()) { |
| error(myExpectedMessage != null ? myExpectedMessage : LangBundle.message("unexpected.eof")); |
| return; |
| } |
| |
| TokenParser parser = findParser(); |
| if (parser == null) { |
| error(myExpectedMessage != null ? myExpectedMessage : LangBundle.message("unexpected.token")); |
| return; |
| } |
| |
| myStartMarker = mark(); |
| while (!isEof()) { |
| int startOffset = myBuilder.getCurrentOffset(); |
| |
| if (!parser.parseToken(this)) break; |
| |
| assert startOffset < myBuilder.getCurrentOffset() : "Endless loop on " + getTokenType(); |
| |
| parser = findParser(); |
| if (parser == null) break; |
| } |
| myStartMarker.drop(); |
| } |
| |
| @Nullable |
| private TokenParser findParser() { |
| final IElementType tokenType = getTokenType(); |
| for (final Trinity<Integer, PathPattern, TokenParser> trinity : myRegistry.getParsers(tokenType)) { |
| if (trinity.first > myPriority && trinity.second.accepts(this)) { |
| return trinity.third; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public void advance() { |
| myLeftSiblings.addLast(getTokenType()); |
| myBuilder.advanceLexer(); |
| } |
| |
| @Override |
| public void error(final String errorText) { |
| final PsiBuilder.Marker marker = myBuilder.mark(); |
| myBuilder.error(errorText); |
| marker.drop(); |
| } |
| |
| @Override |
| @Nullable |
| public IElementType getTokenType() { |
| return myBuilder.getTokenType(); |
| } |
| |
| @Override |
| @Nullable |
| public String getTokenText() { |
| return myBuilder.getTokenText(); |
| } |
| |
| @Override |
| public void reduce(@NotNull final IElementType type) { |
| myStartMarker.finish(type); |
| myStartMarker = myStartMarker.precede(); |
| } |
| |
| @Override |
| @NotNull |
| public List<IElementType> getResultTypes() { |
| checkParsed(); |
| return myLeftSiblings; |
| } |
| |
| private void checkParsed() { |
| if (!myParsingStarted) { |
| myParsingStarted = true; |
| doParse(); |
| } |
| } |
| |
| @Override |
| public PrattBuilder getParent() { |
| return myParentBuilder; |
| } |
| |
| @Override |
| public int getPriority() { |
| return myPriority; |
| } |
| |
| @Override |
| public int getCurrentOffset() { |
| return myBuilder.getCurrentOffset(); |
| } |
| } |