1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-02 00:41:42 +00:00
kickc/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java
2023-04-23 22:04:23 +02:00

443 lines
14 KiB
Java

package dk.camelot64.kickc.model.symbols;
import dk.camelot64.kickc.model.InternalError;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.ScopeRef;
import java.util.*;
/**
* Manages symbols (variables, labels)
*/
public abstract class Scope implements Symbol {
/** The default code segment. */
public static final String SEGMENT_CODE_DEFAULT = "Code";
/** The default data segment. */
public static final String SEGMENT_DATA_DEFAULT = "Data";
private String name;
private HashMap<String, Symbol> symbols;
private int intermediateVarCount = 0;
private int intermediateLabelCount = 1;
private int blockCount = 1;
private Scope parentScope;
private String fullName;
private String segmentData;
public Scope(String name, Scope parentScope, String segmentData) {
this.name = name;
this.parentScope = parentScope;
this.symbols = new LinkedHashMap<>();
this.segmentData = Objects.requireNonNull(segmentData);
setFullName();
}
private void setFullName() {
String scopeName = (parentScope == null) ? "" : parentScope.getFullName();
fullName = (scopeName.length() > 0) ? scopeName + "::" + name : name;
}
public String getSegmentData() {
return segmentData;
}
public void setSegmentData(String segmentData) {
this.segmentData = segmentData;
}
public HashMap<String, Symbol> getSymbols() {
return symbols;
}
@Override
public String getLocalName() {
return name;
}
@Override
public String getFullName() {
return fullName;
}
public ScopeRef getRef() {
return new ScopeRef(this);
}
@Override
public Scope getScope() {
return parentScope;
}
@Override
public void setScope(Scope scope) {
this.parentScope = scope;
setFullName();
}
/**
* Get the top-level program scope.
*
* @return The top-level scope. Null if not found.
*/
public ProgramScope getProgramScope() {
if(this instanceof ProgramScope)
return (ProgramScope) this;
else
return parentScope.getProgramScope();
}
@Override
public abstract SymbolType getType();
@Override
public int getScopeDepth() {
if(parentScope == null) {
return 0;
} else {
return parentScope.getScopeDepth() + 1;
}
}
public void remove(Symbol symbol) {
symbols.remove(symbol.getLocalName());
getProgramScope().clearCache();
}
public <T extends Symbol> T add(T symbol) {
if(symbols.get(symbol.getLocalName()) != null) {
throw new CompileError("Symbol already declared " + symbol.getLocalName());
}
symbols.put(symbol.getLocalName(), symbol);
return symbol;
}
public Label addLabel(String name) {
return add(new Label(name, this, false));
}
public Label addLabelIntermediate() {
String name = "@" + intermediateLabelCount++;
return add(new Label(name, this, true));
}
public BlockScope addBlockScope() {
String name = ":" + blockCount++;
return add(new BlockScope(name, this));
}
public String allocateIntermediateVariableName() {
return "$" + intermediateVarCount++;
}
/**
* Set the value of the counter used to number intermediate labels
*
* @param intermediateLabelCount The new counter value
*/
public void setIntermediateLabelCount(int intermediateLabelCount) {
this.intermediateLabelCount = intermediateLabelCount;
}
/**
* Get all versions of a PHI-master variable
*
* @param unversioned The unversioned PHI-master variable
* @return All versions of the variable
*/
public Collection<Variable> getVersions(Variable unversioned) {
LinkedHashSet<Variable> versions = new LinkedHashSet<>();
for(Symbol symbol : symbols.values()) {
if(symbol instanceof Variable) {
Variable variable = (Variable) symbol;
if(variable.isVariable() && variable.isKindPhiVersion() && variable.getPhiMaster().equals(unversioned)) {
versions.add(variable);
}
}
}
return versions;
}
/**
* Get a local symbol by name. Does not search parent scopes.
*
* @param name The symbol name to look for
* @return The symbol if found. null otherwise.
*/
public Symbol getLocalSymbol(String name) {
return symbols.get(name);
}
/**
* Look for a symbol by it's (short) name. Looks through this scope first and then through any parent scope.
*
* @param name The symbol name
* @return The found symbol. Null is not found.
*/
public Symbol findSymbol(String name) {
Symbol symbol = symbols.get(name);
if(symbol == null) {
if(parentScope != null) {
symbol = parentScope.findSymbol(name);
}
}
return symbol;
}
public Variable findVariable(String name) {
final Symbol symbol = findSymbol(name);
if(symbol != null && !(symbol instanceof Variable))
throw new InternalError("Symbol is not a variable! " + symbol.toString());
return (Variable) symbol;
}
public Collection<Symbol> getAllSymbols() {
return symbols.values();
}
/**
* Get all variables/constants in the scope
*
* @param includeSubScopes true to include sub-scopes
* @return all variables/constants
*/
public Collection<Variable> getAllVars(boolean includeSubScopes) {
Collection<Variable> vars = new ArrayList<>();
for(Symbol symbol : symbols.values()) {
if(symbol instanceof Variable) {
vars.add((Variable) symbol);
}
if(includeSubScopes && symbol instanceof Scope) {
Scope subScope = (Scope) symbol;
vars.addAll(subScope.getAllVars(true));
}
}
return vars;
}
/**
* Get all runtime variables (excluding constants)
*
* @param includeSubScopes true to include sub-scopes
* @return all runtime variables (excluding constants)
*/
public Collection<Variable> getAllVariables(boolean includeSubScopes) {
Collection<Variable> vars = new ArrayList<>();
getAllVars(includeSubScopes).stream().
filter(Variable::isVariable).
forEach(vars::add);
return vars;
}
/**
* Get all constants
*
* @param includeSubScopes true to include sub-scopes
* @return all constants
*/
public Collection<Variable> getAllConstants(boolean includeSubScopes) {
Collection<Variable> vars = new ArrayList<>();
getAllVars(includeSubScopes).stream().
filter(variable -> variable.isKindConstant()).
forEach(vars::add);
return vars;
}
/**
* Get all scopes contained in the scope. This does not include this scope itself.
*
* @param includeSubScopes Include sub-scopes og sub-scopes
* @return The scopes
*/
public Collection<Scope> getAllScopes(boolean includeSubScopes) {
Collection<Scope> scopes = new ArrayList<>();
for(Symbol symbol : symbols.values()) {
if(symbol instanceof Scope) {
scopes.add((Scope) symbol);
if(includeSubScopes) {
Scope subScope = (Scope) symbol;
scopes.addAll(subScope.getAllScopes(true));
}
}
}
return scopes;
}
public Collection<Procedure> getAllProcedures(boolean includeSubScopes) {
Collection<Procedure> procedures = new ArrayList<>();
for(Scope scope : getAllScopes(includeSubScopes)) {
if(scope instanceof Procedure) {
procedures.add((Procedure) scope);
}
}
return procedures;
}
public Collection<Symbol> getAllSymbols(boolean includeSubscopes) {
ArrayList<Symbol> allSymbols = new ArrayList<>();
for(Symbol symbol : symbols.values()) {
allSymbols.add(symbol);
if(symbol instanceof Scope && includeSubscopes) {
allSymbols.addAll(((Scope) symbol).getAllSymbols(true));
}
}
return allSymbols;
}
public Variable getLocalVar(String name) {
final Symbol symbol = getLocalSymbol(name);
if(symbol != null && !(symbol instanceof Variable))
throw new InternalError("Symbol is not a variable! " + symbol.toString());
return (Variable) symbol;
}
public Variable getLocalVariable(String name) {
final Variable var = getLocalVar(name);
if(var != null && !var.isVariable())
throw new InternalError("Symbol is not a variable! " + var.toString());
return var;
}
public Variable getLocalConstant(String name) {
final Variable var = getLocalVar(name);
if(var != null && !var.isKindConstant())
throw new InternalError("Symbol is not a constant! " + var.toString());
return var;
}
public Label getLocalLabel(String name) {
final Symbol symbol = getLocalSymbol(name);
if(symbol != null && !(symbol instanceof Label))
throw new InternalError("Symbol is not a label! " + symbol.toString());
return (Label) getLocalSymbol(name);
}
public BlockScope getLocalBlockScope(String name) {
final Symbol symbol = getLocalSymbol(name);
if(symbol != null && !(symbol instanceof BlockScope))
throw new InternalError("Symbol is not a block scope! " + symbol.toString());
return (BlockScope) symbol;
}
public StructDefinition getLocalStructDefinition(String name) {
final Symbol symbol = getLocalSymbol(name);
if(symbol != null && !(symbol instanceof StructDefinition))
throw new InternalError("Symbol is not a struct definition! " + symbol.toString());
return (StructDefinition) symbol;
}
public EnumDefinition getLocalEnumDefinition(String name) {
final Symbol symbol = getLocalSymbol(name);
if(symbol != null && !(symbol instanceof EnumDefinition))
throw new InternalError("Symbol is not an enum definition! " + symbol.toString());
return (EnumDefinition) symbol;
}
public Scope getLocalScope(String name) {
final Symbol symbol = getLocalSymbol(name);
if(symbol != null && !(symbol instanceof Scope))
throw new InternalError("Symbol is not a scope! " + symbol.toString());
return (Scope) symbol;
}
public String toStringVars(Program program, boolean onlyVars) {
VariableRegisterWeights registerWeights = program.getOrNullVariableRegisterWeights();
StringBuilder res = new StringBuilder();
Set<String> names = symbols.keySet();
List<String> sortedNames = new ArrayList<>(names);
Collections.sort(sortedNames);
for(String name : sortedNames) {
Symbol symbol = symbols.get(name);
if(symbol instanceof Scope) {
// Do not output struct definitions
if(symbol instanceof StructDefinition)
continue;
if(!onlyVars || symbol instanceof Procedure || symbol instanceof BlockScope || symbol instanceof ProgramScope)
res.append(((Scope) symbol).toStringVars(program, onlyVars));
} else if(symbol instanceof Variable) {
Variable symVar = (Variable) symbol;
if(!onlyVars || symVar.isVariable()) {
// Output if not instructed to only output variables - or if it is a variable
if(symVar.isKindLoadStore()) res.append("__loadstore ");
if(symVar.isKindConstant()) res.append("__constant ");
res.append(symVar.getType().toCDecl(symVar.getFullName()));
if(symVar.getInitValue() != null) {
res.append(" = " + symVar.getInitValue().toString(program));
}
boolean extra = false;
if(symVar.getAsmName() != null && !symVar.getName().equals(symVar.getAsmName())) {
if(!extra) { res.append(" //"); extra = true; }
res.append(" " + symVar.getAsmName());
}
Registers.Register declRegister = symVar.getRegister();
if(declRegister != null) {
if(!extra) { res.append(" //"); extra = true; }
res.append(" !" + declRegister);
}
if(symVar.isVariable()) {
Registers.Register register = symVar.getAllocation();
if(register != null && !register.equals(declRegister)) {
if(!extra) { res.append(" //"); extra = true; }
res.append(" " + register);
}
if(registerWeights != null) {
Double weight = registerWeights.getWeight(symVar.getVariableRef());
if(weight != null) {
if(!extra) { res.append(" //"); extra = true; }
res.append(" " + weight);
}
}
}
res.append("\n");
}
} else if(!onlyVars) {
// Do not output labels
if(symbol instanceof Label)
continue;
// Only output if not instructed to only output variables
res.append(symbol.toString(program));
res.append("\n");
}
}
return res.toString();
}
@Override
public boolean equals(Object o) {
if(this == o) {
return true;
}
if(o == null || getClass() != o.getClass()) {
return false;
}
Scope scope = (Scope) o;
if(intermediateVarCount != scope.intermediateVarCount) {
return false;
}
if(intermediateLabelCount != scope.intermediateLabelCount) {
return false;
}
if(!name.equals(scope.name)) {
return false;
}
if(!symbols.equals(scope.symbols)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + intermediateVarCount;
result = 31 * result + intermediateLabelCount;
return result;
}
}