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

net.jangaroo.jooc.JangarooParser Maven / Gradle / Ivy

The newest version!
package net.jangaroo.jooc;

import java_cup.runtime.Symbol;
import net.jangaroo.jooc.api.CompileLog;
import net.jangaroo.jooc.api.FilePosition;
import net.jangaroo.jooc.api.Jooc;
import net.jangaroo.jooc.ast.AstNode;
import net.jangaroo.jooc.ast.ClassDeclaration;
import net.jangaroo.jooc.ast.CompilationUnit;
import net.jangaroo.jooc.ast.Ide;
import net.jangaroo.jooc.ast.IdeDeclaration;
import net.jangaroo.jooc.ast.ImportDirective;
import net.jangaroo.jooc.ast.PredefinedTypeDeclaration;
import net.jangaroo.jooc.ast.TypeDeclaration;
import net.jangaroo.jooc.ast.VariableDeclaration;
import net.jangaroo.jooc.config.ParserOptions;
import net.jangaroo.jooc.config.SemicolonInsertionMode;
import net.jangaroo.jooc.input.InputSource;
import net.jangaroo.jooc.mxml.MxmlComponentRegistry;
import net.jangaroo.properties.Propc;
import net.jangaroo.utils.AS3Type;
import net.jangaroo.utils.BOMStripperInputStream;
import net.jangaroo.utils.CompilerUtils;

import javax.annotation.Nonnull;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class JangarooParser implements CompilationUnitResolver, CompilationUnitRegistry {
  public static final String JOO_API_IN_SWC_DIRECTORY_PREFIX = "META-INF/joo-api/";
  static final String UTF_8 = "UTF-8";

  protected CompileLog log;
  // a hack to always be able to access the current log:
  private static ThreadLocal defaultLog = new ThreadLocal();

  private InputSource sourcePathInputSource;
  private InputSource classPathInputSource;
  private ParserOptions config;
  private Map compilationUnitsByInputSource = new HashMap<>();
  private Map inputSourceByCompilationUnit = new HashMap<>();
  private Map compilationUnitsByQName = new LinkedHashMap();
  private MxmlComponentRegistry mxmlComponentRegistry = new MxmlComponentRegistry();
  private List compilableSuffixes = Arrays.asList(Jooc.PROPERTIES_SUFFIX, Jooc.AS_SUFFIX, Jooc.MXML_SUFFIX);

  private final Scope globalScope = new DeclarationScope(null, null, this);
  private final TypeDeclaration voidType = declareType(globalScope, AS3Type.VOID.toString(), false);
  private final TypeDeclaration anyType = declareType(globalScope, AS3Type.ANY.toString(), true);

  {
    declareValues(globalScope, new String[]{Ide.THIS});
  }

  public JangarooParser(@Nonnull ParserOptions config, @Nonnull CompileLog log) {
    this.config = config;
    this.log = log;
  }

  public TypeDeclaration getAnyType() {
    return anyType;
  }

  public TypeDeclaration getVoidType() {
    return anyType;
  }

  public static CompilerError error(String msg) {
    return new CompilerError(msg);
  }

  public static CompilerError error(String msg, final File file) {
    return new CompilerError(fileToSymbol(file), msg);
  }

  public static CompilerError error(FilePosition symbol, String msg) {
    return new CompilerError(symbol, msg);
  }

  public static CompilerError error(FilePosition symbol, String msg, Throwable t) {
    return new CompilerError(symbol, msg, t);
  }

  public static CompilerError error(AstNode node, String msg) {
    return error(node.getSymbol(), msg);
  }

  public static CompilerError error(String msg, File file, Throwable t) {
    return new CompilerError(fileToSymbol(file), msg, t);
  }

  protected static FilePosition fileToSymbol(File file) {
    return new FilePositionImpl(file);
  }

  public static CompilerError error(String msg, Throwable t) {
    return new CompilerError(msg, t);
  }

  public static void warning(FilePosition symbol, String msg) {
    defaultLog.get().warning(symbol, msg);
  }

  public static void warning(String msg, File file) {
    defaultLog.get().warning(fileToSymbol(file), msg);
  }

  public static void warning(String msg) {
    defaultLog.get().warning(msg);
  }

  public ParserOptions getConfig() {
    return config;
  }

  public void setConfig(ParserOptions config) {
    this.config = config;
  }

  public CompileLog getLog() {
    return log;
  }

  public void setLog(CompileLog log) {
    this.log = log;
  }

  public List getCompilableSuffixes() {
    return compilableSuffixes;
  }

  public void setCompilableSuffixes(List compilableSuffixes) {
    this.compilableSuffixes = compilableSuffixes;
  }

  public CompilationUnit doParse(InputSource in, CompileLog log, SemicolonInsertionMode semicolonInsertionMode) {
    if (config.isVerbose()) {
      System.out.println("Parsing " + in.getPath() + " (" + (in.isInSourcePath() ? "source" : "class") + "path)"); // NOSONAR this is a cmd line tool
    }
    String inputSourceName = in.getName();
    boolean parseMxml = inputSourceName.endsWith(Jooc.MXML_SUFFIX);
    Reader reader;
    try {
      if (inputSourceName.endsWith(Jooc.PROPERTIES_SUFFIX)) {
        reader = createPropertiesClassReader(in);
      } else {
        reader = new InputStreamReader(new BOMStripperInputStream(in.getInputStream()), UTF_8);
      }
    } catch (IOException e) {
      throw new CompilerError("Cannot read input file: " + in.getPath(), e);
    }
    Scanner s = new Scanner(reader);
    s.yybegin(parseMxml ? Scanner.MXML : Scanner.YYINITIAL);
    s.setInputSource(in);
    JooParser p = new JooParser(s);
    p.setCompileLog(log);
    p.setSemicolonInsertionMode(semicolonInsertionMode);
    try {
      Symbol tree = p.parse();
      return (CompilationUnit) tree.value;
    } catch (Scanner.ScanError se) {
      log.error(se.getSym(), se.getMessage());
      return null;
    } catch (JooParser.FatalSyntaxError e) {
      // message already logged in parser
      return null;
    } catch (Exception e) {
      throw new IllegalArgumentException("could not parse Jangaroo source", e);
    }
  }

  public Reader createPropertiesClassReader(InputSource in) throws IOException {
    Propc propertyClassGenerator = new Propc();
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    propertyClassGenerator.generateApi(
            CompilerUtils.qNameFromRelativePath(in.getRelativePath()),
            in.getInputStream(),
            new OutputStreamWriter(outputStream, UTF_8));
    // From the properties file, we generate AS code, then parse a CompilationUnit again.
    String output = outputStream.toString(UTF_8);
    return new StringReader(output);
  }

  private static TypeDeclaration declareType(Scope scope, String identifier, boolean dynamic) {
    TypeDeclaration decl = new PredefinedTypeDeclaration(identifier, dynamic);
    decl.scope(scope);
    return decl;
  }

  protected static void declareValues(Scope scope, String[] identifiers) {
    for (String identifier : identifiers) {
      Ide ide = new Ide(new JooSymbol(identifier));
      IdeDeclaration decl = new VariableDeclaration(new JooSymbol("var"), ide, null, null);
      decl.scope(scope);
    }
  }

  protected InputSource findSource(final String qname) {
    InputSource result;
    // scan sourcepath
    for (String suffix : compilableSuffixes) {
      result = findInputSource(qname, sourcePathInputSource, suffix);
      if (result != null) {
        return result;
      }
    }
    // scan classpath
    for (String suffix : compilableSuffixes) {
      result = findInputSource(qname, classPathInputSource, suffix);
      if (result != null) {
        return result;
      }
    }
    return null;
  }

  private static InputSource findInputSource(String qname, InputSource pathInputSource, String suffix) {
    String inputSourceFileName = getInputSourceFileName(qname, pathInputSource, suffix);
    return inputSourceFileName == null ? null : pathInputSource.getChild(inputSourceFileName);
  }

  public static String getInputSourceFileName(final String qname, InputSource is, String extension) {
    return CompilerUtils.fileNameFromQName(qname, is.getFileSeparatorChar(), extension);
  }

  public CompilationUnit importSource(InputSource source) {
    CompilationUnit unit = compilationUnitsByInputSource.get(source);
    if (unit != null) {
      return unit;
    }

    String fileName = source.getName();
    if (!hasCompilableSuffix(fileName)) {
      throw error("Input file must end with one of '" + getCompilableSuffixes() + "': " + fileName);
    }
    unit = doParse(source, log, config.getSemicolonInsertionMode());
    if (unit == null) {
      return null;
    }

    String qname = unit.getQualifiedNameStr();
    compilationUnitsByQName.put(qname, unit);
    compilationUnitsByInputSource.put(source, unit);
    inputSourceByCompilationUnit.put(unit, source);
    unit.scope(globalScope);
    return unit;
  }

  private boolean hasCompilableSuffix(String fileName) {
    int dotIndex = fileName.lastIndexOf('.');
    return dotIndex != -1 && getCompilableSuffixes().contains(fileName.substring(dotIndex));
  }

  public IdeDeclaration resolveImport(final ImportDirective importDirective) {
    String qname = importDirective.getQualifiedName();
    CompilationUnit compilationUnit;
    try {
      compilationUnit = getCompilationUnit(qname);
    } catch (CompilerError e) {
      getLog().error(e.getSymbol(), e.getMessage());
      throw error(importDirective.getSymbol(), "Unable to import " + qname + ": error while parsing its source (see error above).");
    }
    if (compilationUnit == null) {
      throw error(importDirective.getSymbol(), "unable to resolve import of " + qname);
    }
    return compilationUnit.getPrimaryDeclaration();
  }

  public Collection getCompilationUnits() {
    return compilationUnitsByQName.values();
  }

  public CompilationUnit getCompilationUnit(String qname) {
    CompilationUnit compilationUnit = compilationUnitsByQName.get(qname);
    if (compilationUnit == null) {
      // The compilation unit has not yet been parsed.
      InputSource source = findSource(qname);
      if (source != null) {
        compilationUnit = importSource(source);
      }
    }
    return compilationUnit;
  }

  /**
   * Return true if the argument name identifies a class.
   *
   * @param name the name to check
   * @return whether the argument name identifies a class
   */
  public boolean isClass(String name) {
    if (name == null) {
      return false;
    }

    // fast path for MXML, so they don't get scoped too early:
    CompilationUnit compilationUnit = compilationUnitsByQName.get(name);
    if (compilationUnit == null) {
      // The compilation unit has not yet been parsed.
      InputSource source = findSource(name);
      if (source != null) {
        if (source.getName().endsWith(Jooc.MXML_SUFFIX)) {
          return true;
        }
      }
    }

    return resolveCompilationUnit(name).isClass();
  }


  @Override
  @Nonnull
  public CompilationUnit resolveCompilationUnit(@Nonnull String fullClassName) {
    CompilationUnit compilationUnit = getCompilationUnit(fullClassName);
    if (compilationUnit == null) {
      throw error("Undefined type: " + fullClassName);
    }
    return compilationUnit;
  }

  public MxmlComponentRegistry getMxmlComponentRegistry() {
    return mxmlComponentRegistry;
  }

  public List getPackageIdes(String packageName) {
    List result = new ArrayList(10);
    addPackageFolderSymbols(result, packageName, sourcePathInputSource);
    addPackageFolderSymbols(result, packageName, classPathInputSource);
    return result;
  }

  private void addPackageFolderSymbols(final List result, final String packageName, final InputSource path) {
    final InputSource folder = findInputSource(packageName, path, "");
    if (folder != null) {
      for (InputSource child : folder.list()) {
        if (!child.isDirectory() &&
                hasCompilableSuffix(child.getName())) {
          result.add(CompilerUtils.qNameFromRelativePath(child.getName()));
        }
      }
    }
  }

  public void setUp(InputSource sourcePathInputSource, InputSource classPathInputSource) {
    defaultLog.set(log);
    this.sourcePathInputSource = sourcePathInputSource;
    this.classPathInputSource = classPathInputSource;
  }

  public void tearDown() {
    defaultLog.remove();
  }

  public InputSource getInputSource(final CompilationUnit compilationUnit) {
    return inputSourceByCompilationUnit.get(compilationUnit);
  }

  @Override
  public boolean implementsInterface(CompilationUnit classCompilationUnit, String anInterface) {
    return classCompilationUnit != null && classCompilationUnit.isClass() &&
            implementsInterface((ClassDeclaration) classCompilationUnit.getPrimaryDeclaration(), anInterface);
  }

  public boolean implementsInterface(@Nonnull ClassDeclaration classDeclaration, String anInterface) {
    CompilationUnit interfaceCompilationUnit = getCompilationUnit(anInterface);
    return interfaceCompilationUnit != null && interfaceCompilationUnit.isClass() &&
            classDeclaration.isAssignableTo((ClassDeclaration) interfaceCompilationUnit.getPrimaryDeclaration());
  }

  private static class FilePositionImpl implements FilePosition {
    private final String fileName;
    private final int line;
    private final int column;

    public FilePositionImpl(File file) {
      this(file.getAbsolutePath(), -1, -1);
    }

    public FilePositionImpl(String fileName, int line, int column) {
      this.fileName = fileName;
      this.line = line;
      this.column = column;
    }

    @Override
    public String getFileName() {
      return fileName;
    }

    @Override
    public int getLine() {
      return line;
    }

    @Override
    public int getColumn() {
      return column;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy