blob: baa0fc142bbf378f53050d069af6fcc0a7d23b68 [file] [log] [blame]
/*
* 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();
}
}