Adding an optimization to shorten variable names to 1 or 2 characters.

This allows more useful variable names in the program.
This commit is contained in:
Rob Greene 2018-07-16 22:16:59 -05:00
parent b84896c8cf
commit 9078361a18
7 changed files with 89 additions and 5 deletions

View File

@ -4,6 +4,8 @@ import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class Configuration {
@ -11,6 +13,7 @@ public class Configuration {
public final int startAddress;
public final int maxLineLength;
public final PrintStream debugStream;
public final Map<String,String >variableReplacements = new HashMap<>();
private Configuration(Builder b) {
this.sourceFile = b.sourceFile;

View File

@ -38,6 +38,16 @@ public abstract class Directive {
this.parameterNames = new TreeSet<>(String::compareToIgnoreCase);
this.parameterNames.addAll(Arrays.asList(parameterNames));
}
/** Resolve the given variable name with any variable replacements that should occur. */
public String resolve(String originalVariableName) {
if (config.variableReplacements.containsKey(originalVariableName)) {
String replacementVariableName = config.variableReplacements.get(originalVariableName);
config.debugStream.printf("Replacing '%s' with '%s'\n", originalVariableName, replacementVariableName);
return replacementVariableName;
}
return originalVariableName;
}
public Optional<Expression> optionalExpression(String paramName) {
return Optional.ofNullable(parameters.get(paramName));

View File

@ -3,6 +3,7 @@ package io.github.applecommander.bastools.api;
import java.util.function.Function;
import io.github.applecommander.bastools.api.optimizations.ExtractConstantValues;
import io.github.applecommander.bastools.api.optimizations.ShortenVariableNames;
import io.github.applecommander.bastools.api.optimizations.MergeLines;
import io.github.applecommander.bastools.api.optimizations.RemoveEmptyStatements;
import io.github.applecommander.bastools.api.optimizations.RemoveRemStatements;
@ -15,6 +16,7 @@ import io.github.applecommander.bastools.api.optimizations.Renumber;
public enum Optimization {
REMOVE_EMPTY_STATEMENTS(RemoveEmptyStatements::new),
REMOVE_REM_STATEMENTS(RemoveRemStatements::new),
SHORTEN_VARIABLE_NAMES(ShortenVariableNames::new),
EXTRACT_CONSTANT_VALUES(ExtractConstantValues::new),
MERGE_LINES(MergeLines::new),
RENUMBER(Renumber::new);

View File

@ -42,7 +42,7 @@ public class EmbeddedBinaryDirective extends Directive {
variableName.ifPresent(var -> {
builder.basic()
.assign(var, embeddedStart)
.assign(resolve(var), embeddedStart)
.endStatement();
});

View File

@ -67,7 +67,7 @@ public class EmbeddedShapeTable extends Directive {
// Setup common code
if (poke) basic.POKEW(232, shapeTableStart).endStatement();
if (init) basic.ROT(0).endStatement().SCALE(1).endStatement();
address.ifPresent(var -> basic.assign(var, shapeTableStart).endStatement());
address.ifPresent(var -> basic.assign(resolve(var), shapeTableStart).endStatement());
// Inject src options
assign.ifPresent(expr -> setupVariables(expr, basic, shapeTable));
@ -85,7 +85,7 @@ public class EmbeddedShapeTable extends Directive {
builder.generate(startAddress).writeTo(this.outputStream);
}
public void setupVariables(MapExpression expr, BasicBuilder basic, Optional<ShapeTable> shapeTableOptional) {
ShapeTable st = shapeTableOptional.orElseThrow(() -> new RuntimeException("ShapeTable source not supplied"));
expr.entrySet().forEach(e -> {
@ -93,7 +93,7 @@ public class EmbeddedShapeTable extends Directive {
.map(SimpleExpression::asString)
.orElseThrow(() -> new RuntimeException(
String.format("Unexpected format of asignments for variable '%s'", e.getKey())));
basic.assign(e.getKey(), st.findPositionByLabel(label)).endStatement();
basic.assign(resolve(e.getKey()), st.findPositionByLabel(label)).endStatement();
});
}
@ -104,7 +104,7 @@ public class EmbeddedShapeTable extends Directive {
ShapeTable st = shapeTableOptional.orElseThrow(() -> new RuntimeException("ShapeTable source not supplied"));
for (int i=0; i<st.shapes.size(); i++) {
Shape s = st.shapes.get(i);
basic.assign(s.getLabel(), i+1);
basic.assign(resolve(s.getLabel()), i+1);
}
}

View File

@ -0,0 +1,68 @@
package io.github.applecommander.bastools.api.optimizations;
import java.util.Set;
import io.github.applecommander.bastools.api.Configuration;
import io.github.applecommander.bastools.api.Visitors;
import io.github.applecommander.bastools.api.model.Program;
import io.github.applecommander.bastools.api.model.Statement;
import io.github.applecommander.bastools.api.model.Token;
import io.github.applecommander.bastools.api.model.Token.Type;
import io.github.applecommander.bastools.api.utils.VariableNameGenerator;
import io.github.applecommander.bastools.api.visitors.VariableCollectorVisitor;
/**
* Ensure all variable names are 1 or 2 characters long.
* This allows the source to use more descriptive variable names, which may
* crossover ("PLAYERX" and "PLAYERY" become "PL" as far Applesoft BASIC is
* concerned). Somewhat hampers running without this optimization being used,
* however.
*/
public class ShortenVariableNames extends BaseVisitor {
private Configuration config;
private VariableNameGenerator variableGenerator = new VariableNameGenerator();
public ShortenVariableNames(Configuration config) {
this.config = config;
}
@Override
public Program visit(Program program) {
// Find existing variable names so we don't clobber already existing names
VariableCollectorVisitor collector = Visitors.variableCollectorVisitor();
program.accept(collector);
Set<String> existingVariables = collector.getVariableNames();
// Preassign all variable names
for (String originalName : existingVariables) {
String newName = originalName;
if (newName.replaceAll("[^\\p{Alnum}]","").length() > 2) {
String varType = newName.replaceAll("[\\p{Alnum}]","");
do {
newName = variableGenerator.get().orElseThrow(() -> new RuntimeException("Ran out of variable names to assign"));
newName += varType;
} while (existingVariables.contains(newName));
config.debugStream.printf("Replacing '%s' with '%s'\n", originalName, newName);
}
config.variableReplacements.put(originalName, newName);
}
// Continue walking the tree to replace the variables!
return super.visit(program);
}
@Override
public Statement visit(Statement statement) {
if (statement.tokens.get(0).type == Type.DIRECTIVE) {
return statement;
}
return super.visit(statement);
}
@Override
public Token visit(Token token) {
if (token.type == Type.IDENT) {
return Token.ident(token.line, config.variableReplacements.get(token.text));
}
return super.visit(token);
}
}

View File

@ -79,6 +79,7 @@ public class Main implements Callable<Void> {
"Enable specific optimizations.",
"* @|green remove-empty-statements|@ - Strip out all '::'-like statements.",
"* @|green remove-rem-statements|@ - Remove all REM statements.",
"* @|green shorten-variable-names|@ - Ensure all variables are 1 or 2 characters long.",
"* @|green extract-constant-values|@ - Assign all constant values first.",
"* @|green merge-lines|@ - Merge lines.",
"* @|green renumber|@ - Renumber program."