You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
7.0 KiB
207 lines
7.0 KiB
package chocopy.common.codegen;
|
|
|
|
import chocopy.common.analysis.SymbolTable;
|
|
import chocopy.common.analysis.types.ValueType;
|
|
import chocopy.common.astnodes.Stmt;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.function.Consumer;
|
|
|
|
/**
|
|
* A descriptor for function and method definitions.
|
|
*
|
|
* <p>This class stores information required for code generation such as the information about a
|
|
* function's parameters, local variables, the local symbol table, the function body, and the label
|
|
* where the code for the body is generated.
|
|
*/
|
|
public class FuncInfo extends SymbolInfo {
|
|
|
|
/**
|
|
* The fully-qualified name of the function.
|
|
*
|
|
* <p>All functions in a ChocoPy program have a unique fully-qualified name. Global functions
|
|
* defined with name `f` have fully-qualified name `f`. Methods `m` in a class `C` have
|
|
* fully-qualified name `C.m`. Functions `f` nested inside another function with fully-qualified
|
|
* name `F` have a fully-qualified name of `F.f`.
|
|
*/
|
|
protected final String funcName;
|
|
|
|
/**
|
|
* The static depth of a function.
|
|
*
|
|
* <p>Global functions and class methods have a static depth of 0. Nested functions that are
|
|
* defined in the body of a function with static depth `D` have a static depth of `D+1`.
|
|
*/
|
|
protected final int depth;
|
|
|
|
/** This function's return type. */
|
|
protected final ValueType returnType;
|
|
|
|
/** A list of parameter names. */
|
|
protected final List<String> params = new ArrayList<>();
|
|
|
|
/** A list of local variable descriptors. */
|
|
protected final List<StackVarInfo> locals = new ArrayList<>();
|
|
|
|
/** The function body. */
|
|
protected final List<Stmt> statements = new ArrayList<>();
|
|
|
|
/** The local symbol table that binds identifiers seen in the function's body. */
|
|
protected final SymbolTable<SymbolInfo> symbolTable;
|
|
|
|
/** The label of the generated code for the function's body. */
|
|
protected final Label codeLabel;
|
|
|
|
/** The descriptor of the enclosing function (this is only non-null for nested functions). */
|
|
protected final FuncInfo parentFuncInfo;
|
|
|
|
/**
|
|
* A method that is invoked to emit the function's body.
|
|
*
|
|
* <p>The method should accept one parameter of type `FuncInfo`.
|
|
*/
|
|
protected final Consumer<FuncInfo> emitter;
|
|
|
|
/**
|
|
* Creates a descriptor for a function or method with fully qualified name FUNCNAME returning
|
|
* type RETURNTYPE that is at nesting depth DEPTH. The code label is formed from FUNCNAME by
|
|
* prepending a $ sign to prevent collisions. PARENTSYMBOLTABLE is the symbol table of the
|
|
* containing region. PARENTFUNCINFO is the descriptor of the enclosing function (null for
|
|
* global functions and methods). EMITTER encapsulates a method that emits the function's body
|
|
* (this is usually a generic emitter for user-defined functions/methods, and a special emitter
|
|
* for pre-defined functions/methods).
|
|
*/
|
|
public FuncInfo(
|
|
String funcName,
|
|
int depth,
|
|
ValueType returnType,
|
|
SymbolTable<SymbolInfo> parentSymbolTable,
|
|
FuncInfo parentFuncInfo,
|
|
Consumer<FuncInfo> emitter) {
|
|
this.funcName = funcName;
|
|
this.codeLabel = new Label(String.format("$%s", funcName));
|
|
this.depth = depth;
|
|
this.returnType = returnType;
|
|
this.symbolTable = new SymbolTable<>(parentSymbolTable);
|
|
this.parentFuncInfo = parentFuncInfo;
|
|
this.emitter = emitter;
|
|
}
|
|
|
|
/** Adds parameter with descriptor PARAMINFO to this function. */
|
|
public void addParam(StackVarInfo paramInfo) {
|
|
this.params.add(paramInfo.getVarName());
|
|
this.symbolTable.put(paramInfo.getVarName(), paramInfo);
|
|
}
|
|
|
|
/** Adds a local variable with descriptor STACKVARINFO to this function. */
|
|
public void addLocal(StackVarInfo stackVarInfo) {
|
|
this.locals.add(stackVarInfo);
|
|
this.symbolTable.put(stackVarInfo.getVarName(), stackVarInfo);
|
|
}
|
|
|
|
/** Adds STMTS to the function's body. */
|
|
public void addBody(List<Stmt> stmts) {
|
|
statements.addAll(stmts);
|
|
}
|
|
|
|
/**
|
|
* Returns the index of parameter or local variable NAME in the function's activation record.
|
|
*
|
|
* <p>The convention is that for a function with N params and K local vars, the i`th param is at
|
|
* index `i` and the j`th local var is at index `N+j+2`. In all, a function stores N+K+2
|
|
* variables contiguously in its activation record, where the N+1st is the frame pointer and the
|
|
* N+2nd is the return address.
|
|
*
|
|
* <p>Caution: this is an index (starting at 0), and not an offset in number of bytes.
|
|
*/
|
|
public int getVarIndex(String name) {
|
|
int idx = params.indexOf(name);
|
|
if (idx >= 0) {
|
|
return idx;
|
|
}
|
|
for (int i = 0; i < locals.size(); i++) {
|
|
if (locals.get(i).getVarName().equals(name)) {
|
|
return i + params.size() + 2;
|
|
}
|
|
}
|
|
String msg = String.format("%s is not a var defined in function %s", name, funcName);
|
|
throw new IllegalArgumentException(msg);
|
|
}
|
|
|
|
/** Returns the label corresponding to the function's body in assembly. */
|
|
public Label getCodeLabel() {
|
|
return codeLabel;
|
|
}
|
|
|
|
/**
|
|
* Returns the function's defined name in the program. This is the last component of the
|
|
* dot-separated fully-qualified name.
|
|
*/
|
|
public String getBaseName() {
|
|
int rightmostDotIndex = funcName.lastIndexOf('.');
|
|
if (rightmostDotIndex == -1) {
|
|
return funcName;
|
|
} else {
|
|
return funcName.substring(rightmostDotIndex + 1);
|
|
}
|
|
}
|
|
|
|
/** Returns the function's fully-qualified name. */
|
|
public String getFuncName() {
|
|
return funcName;
|
|
}
|
|
|
|
/** Returns the function's static nesting depth. */
|
|
public int getDepth() {
|
|
return depth;
|
|
}
|
|
|
|
/** Returns the function's parameters in order of definition. */
|
|
public List<String> getParams() {
|
|
return params;
|
|
}
|
|
|
|
/** Returns the return type of this function. */
|
|
public ValueType getReturnType() {
|
|
return returnType;
|
|
}
|
|
|
|
/**
|
|
* Returns the function's explicitly defined local variables, excluding parameters.
|
|
*
|
|
* <p>This list is mainly used in generating code for initializing local variables that are not
|
|
* parameters.
|
|
*/
|
|
public List<StackVarInfo> getLocals() {
|
|
return locals;
|
|
}
|
|
|
|
/** Returns the list of statements in the function's body. */
|
|
public List<Stmt> getStatements() {
|
|
return statements;
|
|
}
|
|
|
|
/**
|
|
* Returns the function's local symbol table.
|
|
*
|
|
* @return the function's local symbol table
|
|
*/
|
|
public SymbolTable<SymbolInfo> getSymbolTable() {
|
|
return symbolTable;
|
|
}
|
|
|
|
/**
|
|
* Returns the parent function's descriptor for nested functions, and null if this function is
|
|
* not nested.
|
|
*/
|
|
public FuncInfo getParentFuncInfo() {
|
|
return parentFuncInfo;
|
|
}
|
|
|
|
/** Emits the function's body. */
|
|
public void emitBody() {
|
|
emitter.accept(this);
|
|
}
|
|
}
|