Skip to content

Commit

Permalink
Improve import file finder
Browse files Browse the repository at this point in the history
  • Loading branch information
Franco Montenegro committed Aug 15, 2016
1 parent eae9b3c commit 9bef526
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 31 deletions.
158 changes: 158 additions & 0 deletions src/main/java/com/ruke/vrjassc/compiler/Compiler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package com.ruke.vrjassc.compiler;

import com.ruke.vrjassc.vrjassc.antlr4.vrjassLexer;
import com.ruke.vrjassc.vrjassc.antlr4.vrjassParser;
import com.ruke.vrjassc.vrjassc.exception.CompileException;
import com.ruke.vrjassc.vrjassc.phase.DefinitionPhase;
import com.ruke.vrjassc.vrjassc.phase.PreprocessorPhase;
import com.ruke.vrjassc.vrjassc.phase.ReferencePhase;
import com.ruke.vrjassc.vrjassc.phase.TranslationPhase;
import com.ruke.vrjassc.vrjassc.symbol.ScopeSymbol;
import com.ruke.vrjassc.vrjassc.symbol.Symbol;
import com.ruke.vrjassc.vrjassc.symbol.VrJassScope;
import com.ruke.vrjassc.vrjassc.util.TokenSymbolBag;
import org.antlr.v4.runtime.*;

import java.io.*;
import java.nio.file.Files;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

public class Compiler {

private File file;

/**
* Symbols (variables, functions, etc.) collected during compilation
*/
private ScopeSymbol symbols = new VrJassScope();

/**
* Symbols to inject (useful to inject natives, ex: common, blizzard)
*/
private ScopeSymbol symbolsToInject = new VrJassScope();

public Compiler(File file) {
this.file = file;
}

public Compiler(String code) {
try {
this.file = File.createTempFile(String.valueOf(code.hashCode()), null);
PrintWriter writer = new PrintWriter(this.file);

writer.write(code);
writer.close();
} catch (Exception e) {

}
}

public File getFile() {
return this.file;
}

public Compiler injectSymbol(Symbol symbol) {
this.symbolsToInject.define(symbol);
return this;
}

public ScopeSymbol getSymbols() {
return this.symbols;
}

public String compile(boolean translate) throws CompileException, IOException {
Set<File> files = new HashSet<File>();

this.symbols.define(this.symbolsToInject);
this.handleImports(this.symbols, files);

if (translate) {
List<String> lines = new LinkedList<String>();

for (File file : files) {
lines.addAll(Files.readAllLines(file.toPath()));
}

vrjassParser parser = this.parser(
new ANTLRInputStream(
String.join("\n", lines) + "\n"
)
);

TokenSymbolBag tokens = new TokenSymbolBag();
ScopeSymbol symbols = new VrJassScope();

symbols.define(this.symbolsToInject);

DefinitionPhase definition = new DefinitionPhase(tokens, symbols);
ReferencePhase reference = new ReferencePhase(tokens, symbols);
TranslationPhase translator = new TranslationPhase(tokens, symbols);

definition.visit(parser.init());
parser.reset();

reference.visit(parser.init());
parser.reset();

return translator.visit(parser.init()).translate();
}

return null;
}

private vrjassParser parser(CharStream stream) throws FileNotFoundException {
Lexer lexer = new vrjassLexer(stream);
TokenStream token = new CommonTokenStream(lexer);

return new vrjassParser(token);
}

/**
* @param symbols Collected symbols
* @param imports Already imported/compiled files
*/
private void handleImports(ScopeSymbol symbols, Set<File> imports) throws IOException {
if (!imports.add(this.file)) {
return;
}

vrjassParser parser = this.parser(
new ANTLRInputStream(
new BufferedReader(new FileReader(this.file))
)
);

TokenSymbolBag tokens = new TokenSymbolBag();

PreprocessorPhase preprocessor = new PreprocessorPhase(this.file);
DefinitionPhase definition = new DefinitionPhase(tokens, symbols);
ReferencePhase reference = new ReferencePhase(tokens, symbols);

parser.removeErrorListeners();

preprocessor.visit(parser.init());
parser.reset();

definition.visit(parser.init());
parser.reset();

for (File file : preprocessor.getFiles()) {
new Compiler(file).handleImports(symbols, imports);
}

// We don't need this phase on natives
if (!this.isNative()) {
reference.visit(parser.init());
parser.reset();
}
}

private boolean isNative() {
String path = this.file.getPath().toLowerCase();
return path.endsWith("common.j") || path.endsWith("blizzard.j");
}

}
51 changes: 28 additions & 23 deletions src/main/java/com/ruke/vrjassc/vrjassc/phase/PreprocessorPhase.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,46 @@

import com.ruke.vrjassc.vrjassc.antlr4.vrjassBaseVisitor;
import com.ruke.vrjassc.vrjassc.antlr4.vrjassParser;
import com.ruke.vrjassc.vrjassc.exception.CompileException;
import com.ruke.vrjassc.vrjassc.exception.ImportException;
import com.ruke.vrjassc.vrjassc.exception.ImportNotFoundException;
import com.ruke.vrjassc.vrjassc.symbol.VrJassScope;
import com.ruke.vrjassc.vrjassc.util.CompilerFacade;
import org.antlr.v4.runtime.ANTLRInputStream;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.File;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;

public class PreprocessorPhase extends vrjassBaseVisitor<Void> {

private CompilerFacade compiler;
private VrJassScope symbols;

public PreprocessorPhase(CompilerFacade compiler, VrJassScope symbols) {
this.compiler = compiler;
this.symbols = symbols;

private File file;

/**
* Imported files
*/
private Set<File> files = new HashSet<File>();

public PreprocessorPhase(File file) {
this.file = file;
}

public Set<File> getFiles() {
return this.files;
}

@Override
public Void visitImportStatement(vrjassParser.ImportStatementContext ctx) {
try {
Path path = Paths.get(ctx.path.getText().replaceAll("\"", ""));
String code = String.join("\n", Files.readAllLines(path)) + "\n";

this.compiler.compile(this.symbols, new ANTLRInputStream(code), true, false, false);
} catch (CompileException e) {
throw new ImportException(ctx.getStart(), e);
} catch (IOException e) {
String path = ctx.path.getText().replaceAll("\"", "");
File _import = this.file.toPath().getParent().resolve(path).toFile();

if (!_import.exists()) {
// If resolving didn't work, try the raw path instead
_import = Paths.get(path).toFile();
}

if (!_import.exists() || !_import.isFile()) {
throw new ImportNotFoundException(ctx.getStart(), ctx.path.getText());
}

this.files.add(_import);

return null;
}
}
33 changes: 28 additions & 5 deletions src/main/java/com/ruke/vrjassc/vrjassc/util/Compile.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package com.ruke.vrjassc.vrjassc.util;

import com.ruke.vrjassc.compiler.Compiler;
import com.ruke.vrjassc.vrjassc.exception.CompileException;
import org.antlr.v4.runtime.ANTLRInputStream;
import com.ruke.vrjassc.vrjassc.symbol.Symbol;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Compile {

private static Symbol natives = null;
private static String commonPath = "./resources/common.j";
private static String blizzardPath = "./resources/blizzard.j";

private CompilerFacade compiler = new CompilerFacade();

Expand All @@ -17,19 +23,36 @@ public CompilerFacade getCompiler() {

public void setCommonPath(String path) {
this.compiler.commonPath = path;
commonPath = path;
}

public void setBlizzardPath(String path) {
this.compiler.blizzardPath = path;
blizzardPath = path;
}

private static Symbol getNativeSymbols() throws IOException {
if (natives == null) {
Compiler common = new Compiler(new File(commonPath));
common.compile(false);

Compiler blizzard = new Compiler(new File(blizzardPath));
blizzard.injectSymbol(common.getSymbols());
blizzard.compile(false);

natives = blizzard.getSymbols();
}

return natives;
}

public String run(String code, boolean translate) throws CompileException, IOException {
String replacedTabs = code.replace("\t", " ") + "\n";
ANTLRInputStream is = new ANTLRInputStream(replacedTabs);

this.compiler.translateCode = translate;

return this.compiler.compile(is);
Compiler output = new Compiler(replacedTabs);
output.injectSymbol(getNativeSymbols());

return output.compile(translate);
}

public String run(String code) throws CompileException, IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import java.nio.file.Paths;
import java.util.HashSet;

/**
* @deprecated Use Compiler.Compiler instead
*/
public class CompilerFacade {

static VrJassScope nativeScope = null;
Expand Down Expand Up @@ -106,7 +109,7 @@ public String compile(VrJassScope symbols, ANTLRInputStream is, boolean beingImp

symbols.define(this.getNatives());

PreprocessorPhase procPhase = new PreprocessorPhase(this, symbols);
PreprocessorPhase procPhase = new PreprocessorPhase(null);
DefinitionPhase defPhase = new DefinitionPhase(tokenBag, symbols);
ReferencePhase refPhase = new ReferencePhase(tokenBag, symbols);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.ruke.vrjassc.vrjassc.util.ProjectPath;
import com.ruke.vrjassc.vrjassc.util.TestHelper;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;

import java.net.URL;
Expand All @@ -15,15 +16,15 @@ public void nestedImport() {
}

@Test
@Ignore
public void shouldIncludeCodeOnce() {
String path = ProjectPath.getTest() + "/compiler/import-test.j";

String code =
"function bar\n" +
"call foo()\n" +
"end\n" +
"import \"" + path + "\"\n" +
"import \"" + path + "\"";
"import \"" + path + "\"\n";

String expected =
"globals\n" +
Expand Down

0 comments on commit 9bef526

Please sign in to comment.