All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.javascript.parser.sslr.ActionParser2 Maven / Gradle / Ivy

There is a newer version: 2.5
Show 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();
    }

  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy