org.sonar.javascript.parser.sslr.ActionParser2 Maven / Gradle / Ivy
The newest version!
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011 SonarSource and Eriks Nukis
* [email protected]
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.javascript.parser.sslr;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import com.sonar.sslr.api.AstNode;
import com.sonar.sslr.api.Grammar;
import com.sonar.sslr.api.RecognitionException;
import com.sonar.sslr.api.Rule;
import com.sonar.sslr.impl.Parser;
import com.sonar.sslr.impl.matcher.RuleDefinition;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.sonar.javascript.ast.parser.AstNodeSanitizer;
import org.sonar.javascript.parser.sslr.DelayedRuleInvocationExpression;
import org.sonar.javascript.parser.sslr.GrammarBuilder;
import org.sonar.javascript.parser.sslr.Input;
import org.sonar.javascript.parser.sslr.NonterminalBuilder;
import org.sonar.javascript.parser.sslr.Optional;
import org.sonar.sslr.grammar.GrammarRuleKey;
import org.sonar.sslr.grammar.LexerlessGrammarBuilder;
import org.sonar.sslr.internal.matchers.InputBuffer;
import org.sonar.sslr.internal.vm.FirstOfExpression;
import org.sonar.sslr.internal.vm.ParsingExpression;
import org.sonar.sslr.internal.vm.SequenceExpression;
import org.sonar.sslr.internal.vm.StringExpression;
import org.sonar.sslr.parser.ParseError;
import org.sonar.sslr.parser.ParseErrorFormatter;
import org.sonar.sslr.parser.ParseRunner;
import org.sonar.sslr.parser.ParsingResult;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Set;
public class ActionParser2 extends Parser {
private final Charset charset;
private final AstNodeSanitizer astNodeSanitzer = new AstNodeSanitizer();
private final GrammarBuilderInterceptor grammarBuilderInterceptor;
private final SyntaxTreeCreator syntaxTreeCreator;
private final GrammarRuleKey rootRule;
private final Grammar grammar;
private final ParseRunner parseRunner;
public ActionParser2(Charset charset, LexerlessGrammarBuilder b, Class grammarClass, Object treeFactory, GrammarRuleKey rootRule) {
super(null);
this.charset = charset;
this.grammarBuilderInterceptor = new GrammarBuilderInterceptor(b);
Enhancer grammarEnhancer = new Enhancer();
grammarEnhancer.setSuperclass(grammarClass);
grammarEnhancer.setCallback(grammarBuilderInterceptor);
ActionMethodInterceptor actionMethodInterceptor = new ActionMethodInterceptor(grammarBuilderInterceptor);
Enhancer actionEnhancer = new Enhancer();
actionEnhancer.setSuperclass(treeFactory.getClass());
actionEnhancer.setCallback(actionMethodInterceptor);
Object grammar = grammarEnhancer.create(
new Class[] {GrammarBuilder.class, treeFactory.getClass()},
new Object[] {grammarBuilderInterceptor, actionEnhancer.create()});
for (Method method : grammarClass.getMethods()) {
if (method.getDeclaringClass().equals(Object.class)) {
continue;
}
try {
method.invoke(grammar);
} catch (InvocationTargetException e) {
throw Throwables.propagate(e);
} catch (IllegalAccessException e) {
throw Throwables.propagate(e);
}
}
this.syntaxTreeCreator = new SyntaxTreeCreator(treeFactory, grammarBuilderInterceptor);
b.setRootRule(rootRule);
this.rootRule = rootRule;
this.grammar = b.build();
this.parseRunner = new ParseRunner(this.grammar.getRootRule());
}
@Override
public AstNode parse(List tokens) {
throw new UnsupportedOperationException();
}
@Override
public AstNode parse(File file) {
try {
return parse(new Input(Files.toString(file, charset).toCharArray(), file.toURI()));
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
@Override
public AstNode parse(String source) {
return parse(new Input(source.toCharArray()));
}
private AstNode parse(Input input) {
ParsingResult result = parseRunner.parse(input.input());
if (!result.isMatched()) {
ParseError parseError = result.getParseError();
InputBuffer inputBuffer = parseError.getInputBuffer();
int line = inputBuffer.getPosition(parseError.getErrorIndex()).getLine();
String message = new ParseErrorFormatter().format(parseError);
throw new RecognitionException(line, message);
}
AstNode astNode = syntaxTreeCreator.create(result.getParseTreeRoot(), input);
astNodeSanitzer.sanitize(astNode);
return astNode;
}
@Override
public Grammar getGrammar() {
return grammar;
}
@Override
public void setRootRule(Rule rootRule) {
throw new UnsupportedOperationException();
}
@Override
public RuleDefinition getRootRule() {
throw new UnsupportedOperationException();
}
public GrammarRuleKey rootRule() {
return rootRule;
}
public static class GrammarBuilderInterceptor implements MethodInterceptor, GrammarBuilder, NonterminalBuilder {
private final LexerlessGrammarBuilder b;
private final BiMap mapping = HashBiMap.create();
private final BiMap actions = HashBiMap.create();
private final Set optionals = Sets.newHashSet();
private final Set oneOrMores = Sets.newHashSet();
private final Set zeroOrMores = Sets.newHashSet();
private Method buildingMethod = null;
private GrammarRuleKey ruleKey = null;
private final Deque expressionStack = new ArrayDeque();
public GrammarBuilderInterceptor(LexerlessGrammarBuilder b) {
this.b = b;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getDeclaringClass().equals(Object.class)) {
return proxy.invokeSuper(obj, args);
}
if (buildingMethod != null) {
push(new DelayedRuleInvocationExpression(b, this, method));
return null;
}
buildingMethod = method;
return proxy.invokeSuper(obj, args);
}
@Override
public NonterminalBuilder nonterminal() {
return nonterminal(new DummyGrammarRuleKey(this.buildingMethod));
}
@Override
public NonterminalBuilder nonterminal(GrammarRuleKey ruleKey) {
this.ruleKey = ruleKey;
this.mapping.put(this.buildingMethod, this.ruleKey);
return this;
}
@Override
public Object is(Object method) {
Preconditions.checkState(expressionStack.size() == 1, "Unexpected stack size: " + expressionStack.size());
ParsingExpression expression = pop();
b.rule(ruleKey).is(expression);
this.buildingMethod = null;
this.ruleKey = null;
return null;
}
@Override
public T firstOf(T... methods) {
ParsingExpression expression = new FirstOfExpression(pop(methods.length));
expressionStack.push(expression);
return null;
}
@Override
public Optional optional(T method) {
ParsingExpression expression = pop();
GrammarRuleKey ruleKey = new DummyGrammarRuleKey("optional", expression);
optionals.add(ruleKey);
b.rule(ruleKey).is(b.optional(expression));
invokeRule(ruleKey);
return null;
}
@Override
public List oneOrMore(T method) {
ParsingExpression expression = pop();
GrammarRuleKey ruleKey = new DummyGrammarRuleKey("oneOrMore", expression);
oneOrMores.add(ruleKey);
b.rule(ruleKey).is(b.oneOrMore(expression));
invokeRule(ruleKey);
return null;
}
@Override
public Optional> zeroOrMore(T method) {
ParsingExpression expression = pop();
GrammarRuleKey ruleKey = new DummyGrammarRuleKey("zeroOrMore", expression);
zeroOrMores.add(ruleKey);
b.rule(ruleKey).is(b.zeroOrMore(expression));
invokeRule(ruleKey);
return null;
}
@Override
public AstNode invokeRule(GrammarRuleKey ruleKey) {
push(new DelayedRuleInvocationExpression(b, ruleKey));
return null;
}
@Override
public AstNode token(String value) {
expressionStack.push(new StringExpression(value));
return null;
}
public void replaceByRule(GrammarRuleKey ruleKey, int stackElements) {
ParsingExpression expression = stackElements == 1 ? pop() : new SequenceExpression(pop(stackElements));
b.rule(ruleKey).is(expression);
invokeRule(ruleKey);
}
private ParsingExpression[] pop(int n) {
ParsingExpression[] result = new ParsingExpression[n];
for (int i = n - 1; i >= 0; i--) {
result[i] = pop();
}
return result;
}
private ParsingExpression pop() {
return expressionStack.pop();
}
private void push(ParsingExpression expression) {
expressionStack.push(expression);
}
public GrammarRuleKey ruleKeyForAction(Method method) {
GrammarRuleKey ruleKey = actions.get(method);
if (ruleKey == null) {
method.setAccessible(true);
ruleKey = new DummyGrammarRuleKey(method);
actions.put(method, ruleKey);
}
return ruleKey;
}
@Nullable
public Method actionForRuleKey(Object ruleKey) {
return actions.inverse().get(ruleKey);
}
@Nullable
public GrammarRuleKey ruleKeyForMethod(Method method) {
return mapping.get(method);
}
public boolean hasMethodForRuleKey(Object ruleKey) {
return mapping.containsValue(ruleKey);
}
public boolean isOptionalRule(Object ruleKey) {
return optionals.contains(ruleKey);
}
public boolean isOneOrMoreRule(Object ruleKey) {
return oneOrMores.contains(ruleKey);
}
public boolean isZeroOrMoreRule(Object ruleKey) {
return zeroOrMores.contains(ruleKey);
}
}
public static class ActionMethodInterceptor implements MethodInterceptor {
private final GrammarBuilderInterceptor grammarBuilderInterceptor;
public ActionMethodInterceptor(GrammarBuilderInterceptor grammarBuilderInterceptor) {
this.grammarBuilderInterceptor = grammarBuilderInterceptor;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getDeclaringClass().equals(Object.class)) {
return proxy.invokeSuper(obj, args);
}
GrammarRuleKey ruleKey = grammarBuilderInterceptor.ruleKeyForAction(method);
grammarBuilderInterceptor.replaceByRule(ruleKey, args.length);
return null;
}
}
private static class DummyGrammarRuleKey implements GrammarRuleKey {
private final Method method;
private final String operator;
private final ParsingExpression expression;
public DummyGrammarRuleKey(Method method) {
this.method = method;
this.operator = null;
this.expression = null;
}
public DummyGrammarRuleKey(String operator, ParsingExpression expression) {
this.method = null;
this.operator = operator;
this.expression = expression;
}
@Override
public String toString() {
if (operator != null) {
return operator + "(" + expression + ")";
}
StringBuilder sb = new StringBuilder();
sb.append("f.");
sb.append(method.getName());
sb.append('(');
Class[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length - 1; i++) {
sb.append(parameterTypes[i].getSimpleName());
sb.append(", ");
}
if (parameterTypes.length > 0) {
sb.append(parameterTypes[parameterTypes.length - 1].getSimpleName());
}
sb.append(')');
return sb.toString();
}
}
}