Initial commit of main compiler sources (or should I say ... SAUCES!)
This commit is contained in:
81
slangc/model/clauses/Arguments.sauce
Normal file
81
slangc/model/clauses/Arguments.sauce
Normal file
@@ -0,0 +1,81 @@
|
||||
package slangc.model.clauses;
|
||||
|
||||
import slangc.api.BytecodeInstructionWriter;
|
||||
import slangc.bytecode.MethodSignature;
|
||||
import slangc.bytecode.TypeSignature;
|
||||
import slangc.model.ExpressionModel;
|
||||
import slangc.model.ExpressionOwner;
|
||||
import slangc.model.InnerTypeScope;
|
||||
import slangc.model.MemberModel;
|
||||
import slangc.model.MethodModel;
|
||||
import slangc.model.ParameterSet;
|
||||
import slangc.model.StatementOwner;
|
||||
import slangc.model.TypeModel;
|
||||
import slangc.parser.Branch;
|
||||
import slangc.parser.Node;
|
||||
|
||||
public class Arguments extends Expressions implements ExpressionOwner {
|
||||
|
||||
public Arguments(ExpressionOwner owner, Branch source) {
|
||||
super(owner, (Branch)source.getSubnode(1));
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public InnerTypeScope getTypeScope() {
|
||||
return getOwner().getTypeScope();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeModel resolveType(Node subnode) {
|
||||
return getOwner().resolveType(subnode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemberModel getMethodOrField() {
|
||||
return getOwner().getMethodOrField();
|
||||
}
|
||||
|
||||
public void generate(BytecodeInstructionWriter w, MethodModel method, boolean allowVarargs) {
|
||||
ParameterSet params = method.getParameters();
|
||||
for (int i = 0; i < params.countNonVarargParameters(); i++) {
|
||||
ExpressionModel e = getExpression(i);
|
||||
TypeSignature et = e.generate(w);
|
||||
if (!et.mappableEquals(params.getParameter(i).getStorageType().getTypeSignature())) {
|
||||
w.genConvert(et, params.getParameter(i).getStorageType().getTypeSignature());
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowVarargs ||
|
||||
(params.isVarargs()
|
||||
&& params.countParameters() == countExpressions()
|
||||
&& getExpression(params.countNonVarargParameters()).getResult()
|
||||
.getValueTypeWithNullType(params.getVarargsParameter().getStorageType()).getTypeSignature()
|
||||
== params.getVarargsParameter().getStorageType().getTypeSignature())) {
|
||||
ExpressionModel e = getExpression(params.countNonVarargParameters());
|
||||
TypeSignature et = e.generate(w);
|
||||
if (!et.mappableEquals(params.getParameter(params.countNonVarargParameters()).getStorageType().getTypeSignature())) {
|
||||
w.genConvert(et, params.getParameter(params.countNonVarargParameters()).getStorageType().getTypeSignature());
|
||||
}
|
||||
} else if (allowVarargs && params.isVarargs()) {
|
||||
TypeSignature arrtype = params.getVarargsParameter().getStorageType().getTypeSignature();
|
||||
w.genConst(w.getTarget().getHeap().getConstInt32("" + (countExpressions() - params.countNonVarargParameters())));
|
||||
w.genNewArray(arrtype);
|
||||
|
||||
for (int i = params.countNonVarargParameters(); i < countExpressions(); i++) {
|
||||
w.genDup(arrtype);
|
||||
ExpressionModel e = getExpression(i);
|
||||
TypeSignature et = e.generate(w);
|
||||
if (!et.mappableEquals(params.getVarargsElementType().getTypeSignature())) {
|
||||
w.genConvert(et, params.getVarargsElementType().getTypeSignature());
|
||||
et = params.getVarargsElementType().getTypeSignature();
|
||||
}
|
||||
w.genSwap(et, arrtype);
|
||||
w.genConst(w.getTarget().getHeap().getConstInt32("" + (i - params.countNonVarargParameters())));
|
||||
//w.genSwap(getSystem().getDefaultInt32Type().getTypeSignature(), getResult().getValueType().getTypeSignature());
|
||||
w.genArrayStore(params.getVarargsElementType().getTypeSignature());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
75
slangc/model/clauses/CatchClause.sauce
Normal file
75
slangc/model/clauses/CatchClause.sauce
Normal file
@@ -0,0 +1,75 @@
|
||||
package slangc.model.clauses;
|
||||
|
||||
import slangc.api.Reporter;
|
||||
import slangc.model.ClauseModel;
|
||||
import slangc.model.InnerTypeScope;
|
||||
import slangc.model.LocalVariableSlot;
|
||||
import slangc.model.MemberModel;
|
||||
import slangc.model.MethodModel;
|
||||
import slangc.model.Named;
|
||||
import slangc.model.StatementModel;
|
||||
import slangc.model.StatementOwner;
|
||||
import slangc.model.TypeModel;
|
||||
import slangc.parser.Branch;
|
||||
import slangc.parser.Node;
|
||||
|
||||
public class CatchClause extends ClauseModel implements StatementOwner {
|
||||
private Variables variables;
|
||||
private StatementModel innerStatement;
|
||||
|
||||
public CatchClause(StatementOwner owner, Branch source) {
|
||||
super(owner, source);
|
||||
// TODO: Handle catch parameter...
|
||||
//sourceFile.annotate(ErrorType.INTERNAL_ERROR, "TODO: Finish catch clause");
|
||||
variables = (Variables) ClauseModel.construct(this, ((Branch)((Branch)source.getSubnode(1)).getSubnode(1)).getSubnode(0));
|
||||
innerStatement = StatementModel.construct(this, source.getSubnode(2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Reporter reporter, String indent, String incr) {
|
||||
reporter.note("DUMP", indent + "> Catch:");
|
||||
variables.dump(reporter, indent + incr, incr);
|
||||
innerStatement.dump(reporter, indent + incr, incr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int resolveExpressions() {
|
||||
return variables.resolveExpressions() + innerStatement.resolveExpressions();
|
||||
}
|
||||
|
||||
//@Override
|
||||
public TypeModel resolveType(Node subnode) {
|
||||
return getOwner().resolveType(subnode);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public MemberModel getMethodOrField() {
|
||||
return getOwner().getMethodOrField();
|
||||
}
|
||||
|
||||
|
||||
//@Override
|
||||
public InnerTypeScope getTypeScope() {
|
||||
return getOwner().getTypeScope();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Named lookupSimpleName(String name) {
|
||||
if (variables.hasVariable(name)) {
|
||||
return variables.getVariable(name);
|
||||
}
|
||||
return super.lookupSimpleName(name);
|
||||
}
|
||||
|
||||
public StatementModel getInnerStatement() {
|
||||
return innerStatement;
|
||||
}
|
||||
|
||||
public LocalVariableSlot getCatchVariable() {
|
||||
return variables.getVariable(0);
|
||||
}
|
||||
|
||||
public TypeModel getCatchType() {
|
||||
return variables.getVariable(0).getStorageType();
|
||||
}
|
||||
}
|
51
slangc/model/clauses/ElseClause.sauce
Normal file
51
slangc/model/clauses/ElseClause.sauce
Normal file
@@ -0,0 +1,51 @@
|
||||
package slangc.model.clauses;
|
||||
|
||||
import slangc.api.Reporter;
|
||||
import slangc.model.ClauseModel;
|
||||
import slangc.model.InnerTypeScope;
|
||||
import slangc.model.MemberModel;
|
||||
import slangc.model.StatementModel;
|
||||
import slangc.model.StatementOwner;
|
||||
import slangc.model.TypeModel;
|
||||
import slangc.parser.Branch;
|
||||
import slangc.parser.Node;
|
||||
|
||||
public class ElseClause extends ClauseModel implements StatementOwner {
|
||||
private StatementModel innerStatement;
|
||||
|
||||
public ElseClause(StatementOwner owner, Branch source) {
|
||||
super(owner, source);
|
||||
|
||||
innerStatement = StatementModel.construct(this, source.getSubnode(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Reporter reporter, String indent, String incr) {
|
||||
reporter.note("DUMP", indent + "> Else:");
|
||||
innerStatement.dump(reporter, indent + incr, incr);
|
||||
}
|
||||
@Override
|
||||
public int resolveExpressions() {
|
||||
return innerStatement.resolveExpressions();
|
||||
}
|
||||
|
||||
//@Override
|
||||
public TypeModel resolveType(Node subnode) {
|
||||
return getOwner().resolveType(subnode);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public MemberModel getMethodOrField() {
|
||||
return getOwner().getMethodOrField();
|
||||
}
|
||||
|
||||
|
||||
//@Override
|
||||
public InnerTypeScope getTypeScope() {
|
||||
return getOwner().getTypeScope();
|
||||
}
|
||||
|
||||
public StatementModel getInnerStatement() {
|
||||
return innerStatement;
|
||||
}
|
||||
}
|
106
slangc/model/clauses/Expressions.sauce
Normal file
106
slangc/model/clauses/Expressions.sauce
Normal file
@@ -0,0 +1,106 @@
|
||||
package slangc.model.clauses;
|
||||
|
||||
import slang.data.List;
|
||||
import slangc.api.Reporter;
|
||||
import slangc.model.ExpressionModel;
|
||||
import slangc.model.ExpressionOwner;
|
||||
import slangc.model.InnerTypeScope;
|
||||
import slangc.model.MemberModel;
|
||||
import slangc.model.StatementOwner;
|
||||
import slangc.model.TypeModel;
|
||||
import slangc.parser.Branch;
|
||||
import slangc.parser.Node;
|
||||
import slangc.parser.NodeType;
|
||||
|
||||
public class Expressions extends ExpressionsOrVariables implements ExpressionOwner {
|
||||
private List<ExpressionModel> exprs = new List<ExpressionModel>();
|
||||
public Expressions(ExpressionOwner owner, Branch source) {
|
||||
super(owner, source);
|
||||
for (int i = 0; i < source.countSubnodes(); i++) {
|
||||
Node n = source.getSubnode(i);
|
||||
if (n.getNodeType() != NodeType.COMMA) {
|
||||
exprs.append(ExpressionModel.construct(this, n));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void dump(Reporter reporter, String indent, String incr) {
|
||||
reporter.note("DUMP", indent + "> Expressions:");
|
||||
// do not recurse: type.dump(reporter, indent + incr, incr);
|
||||
for (int i = 0; i < exprs.count(); i++) {
|
||||
exprs.get(i).dump(reporter, indent + incr, incr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int resolveExpressions() {
|
||||
int nresolved = 0;
|
||||
for (int i = 0; i < exprs.count(); i++) {
|
||||
nresolved += exprs.get(i).resolveExpressions();
|
||||
}
|
||||
return nresolved;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public TypeModel getExpectedResult(ExpressionModel e) {
|
||||
return getOwner().getExpectedResult(e);
|
||||
}
|
||||
|
||||
|
||||
//@Override
|
||||
public InnerTypeScope getTypeScope() {
|
||||
return getOwner().getTypeScope();
|
||||
}
|
||||
|
||||
|
||||
//@Override
|
||||
public TypeModel resolveType(Node subnode) {
|
||||
return getOwner().resolveType(subnode);
|
||||
}
|
||||
|
||||
|
||||
//@Override
|
||||
public MemberModel getMethodOrField() {
|
||||
return getOwner().getMethodOrField();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionOwner getOwner() {
|
||||
// TODO Auto-generated method stub
|
||||
return (ExpressionOwner) super.getOwner();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//@Override
|
||||
public ExpressionModel[] getSubexpressions() {
|
||||
ExpressionModel[] result = new ExpressionModel[exprs.count()];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = exprs.get(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String resultString() {
|
||||
String r = "(";
|
||||
|
||||
for (int i = 0; i < exprs.count(); i++) {
|
||||
if (i != 0) {
|
||||
r += ",";
|
||||
}
|
||||
r += exprs.get(i).getResult();
|
||||
}
|
||||
|
||||
return r + ")";
|
||||
}
|
||||
|
||||
public int countExpressions() {
|
||||
return exprs.count();
|
||||
}
|
||||
|
||||
public ExpressionModel getExpression(int i) {
|
||||
return exprs.get(i);
|
||||
}
|
||||
}
|
16
slangc/model/clauses/ExpressionsOrVariables.sauce
Normal file
16
slangc/model/clauses/ExpressionsOrVariables.sauce
Normal file
@@ -0,0 +1,16 @@
|
||||
package slangc.model.clauses;
|
||||
|
||||
import slangc.model.ClauseModel;
|
||||
import slangc.model.StatementOwner;
|
||||
import slangc.parser.Branch;
|
||||
|
||||
/** Used as an internal clause primarily in "for" statements where the parts inside (...;...;...) might be
|
||||
* variable definitions or might be plain old expressions (or might just be empty, i.e. zero expressions).
|
||||
*/
|
||||
public abstract class ExpressionsOrVariables extends ClauseModel {
|
||||
|
||||
public ExpressionsOrVariables(StatementOwner owner, Branch source) {
|
||||
super(owner, source);
|
||||
}
|
||||
|
||||
}
|
52
slangc/model/clauses/FinallyClause.sauce
Normal file
52
slangc/model/clauses/FinallyClause.sauce
Normal file
@@ -0,0 +1,52 @@
|
||||
package slangc.model.clauses;
|
||||
|
||||
import slangc.api.Reporter;
|
||||
import slangc.model.ClauseModel;
|
||||
import slangc.model.InnerTypeScope;
|
||||
import slangc.model.MemberModel;
|
||||
import slangc.model.StatementModel;
|
||||
import slangc.model.StatementOwner;
|
||||
import slangc.model.TypeModel;
|
||||
import slangc.parser.Branch;
|
||||
import slangc.parser.Node;
|
||||
|
||||
public class FinallyClause extends ClauseModel implements StatementOwner {
|
||||
private StatementModel innerStatement;
|
||||
|
||||
public FinallyClause(StatementOwner owner, Branch source) {
|
||||
super(owner, source);
|
||||
|
||||
innerStatement = StatementModel.construct(this, source.getSubnode(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Reporter reporter, String indent, String incr) {
|
||||
reporter.note("DUMP", indent + "> Finally:");
|
||||
innerStatement.dump(reporter, indent + incr, incr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int resolveExpressions() {
|
||||
return innerStatement.resolveExpressions();
|
||||
}
|
||||
|
||||
//@Override
|
||||
public TypeModel resolveType(Node subnode) {
|
||||
return getOwner().resolveType(subnode);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public MemberModel getMethodOrField() {
|
||||
return getOwner().getMethodOrField();
|
||||
}
|
||||
|
||||
|
||||
//@Override
|
||||
public InnerTypeScope getTypeScope() {
|
||||
return getOwner().getTypeScope();
|
||||
}
|
||||
|
||||
public StatementModel getInnerStatement() {
|
||||
return innerStatement;
|
||||
}
|
||||
}
|
95
slangc/model/clauses/SwitchMembers.sauce
Normal file
95
slangc/model/clauses/SwitchMembers.sauce
Normal file
@@ -0,0 +1,95 @@
|
||||
package slangc.model.clauses;
|
||||
|
||||
import slang.data.List;
|
||||
import slangc.api.Reporter;
|
||||
import slangc.model.ClauseModel;
|
||||
import slangc.model.InnerTypeScope;
|
||||
import slangc.model.MemberModel;
|
||||
import slangc.model.Named;
|
||||
import slangc.model.StatementModel;
|
||||
import slangc.model.StatementOwner;
|
||||
import slangc.model.TypeModel;
|
||||
import slangc.model.InnerTypeScope.SourceScope;
|
||||
import slangc.model.statements.VariableStatement;
|
||||
import slangc.parser.Branch;
|
||||
import slangc.parser.Node;
|
||||
import slangc.parser.NodeType;
|
||||
|
||||
public class SwitchMembers extends ClauseModel implements StatementOwner {
|
||||
private List<StatementModel> statements = new List<StatementModel>();
|
||||
private InnerTypeScope.SourceScope typeScope;
|
||||
|
||||
public SwitchMembers(StatementOwner owner, Branch source) {
|
||||
super(owner, source);
|
||||
for (int i = 0; i < source.countSubnodes(); i++) {
|
||||
if (source.getSubnode(i).getNodeType() != NodeType.SEMICOLON) {
|
||||
statements.append(StatementModel.construct(this, source.getSubnode(i)));
|
||||
}
|
||||
}
|
||||
typeScope = owner.getTypeScope().getInnerScopeForSource(source);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public InnerTypeScope getTypeScope() {
|
||||
return typeScope == null ? getOwner().getTypeScope() : typeScope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Reporter reporter, String indent, String incr) {
|
||||
reporter.note("DUMP", indent + "> Switch members:");
|
||||
for (int i = 0; i < statements.count(); i++) {
|
||||
StatementModel s = statements.get(i);
|
||||
s.dump(reporter, indent + incr, incr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int resolveExpressions() {
|
||||
int nresolved = 0;
|
||||
|
||||
for (int i = 0; i < statements.count(); i++) {
|
||||
StatementModel s = statements.get(i);
|
||||
nresolved += s.resolveExpressions();
|
||||
}
|
||||
|
||||
return nresolved;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public TypeModel resolveType(Node subnode) {
|
||||
return getTypeScope().resolveTypeReference(subnode);
|
||||
}
|
||||
//@Override
|
||||
public MemberModel getMethodOrField() {
|
||||
return getOwner().getMethodOrField();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Named lookupSimpleName(String name) {
|
||||
for (int i = 0; i < statements.count(); i++) {
|
||||
StatementModel s = statements.get(i);
|
||||
if (s instanceof VariableStatement) {
|
||||
VariableStatement vs = (VariableStatement) s;
|
||||
if (vs.hasVariable(name)) {
|
||||
return vs.getVariable(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeScope != null) {
|
||||
if (typeScope.hasInnerType(name)) {
|
||||
return typeScope.getInnerType(name);
|
||||
}
|
||||
}
|
||||
|
||||
return super.lookupSimpleName(name);
|
||||
}
|
||||
|
||||
public int countMembers() {
|
||||
return statements.count();
|
||||
}
|
||||
|
||||
public StatementModel getMember(int i) {
|
||||
return statements.get(i);
|
||||
}
|
||||
}
|
25
slangc/model/clauses/UnrecognisedClause.sauce
Normal file
25
slangc/model/clauses/UnrecognisedClause.sauce
Normal file
@@ -0,0 +1,25 @@
|
||||
package slangc.model.clauses;
|
||||
|
||||
import slangc.api.Reporter;
|
||||
import slangc.model.ClauseModel;
|
||||
import slangc.model.StatementOwner;
|
||||
import slangc.parser.ErrorType;
|
||||
import slangc.parser.Node;
|
||||
|
||||
public class UnrecognisedClause extends ClauseModel {
|
||||
|
||||
public UnrecognisedClause(StatementOwner owner, Node source) {
|
||||
super(owner, source);
|
||||
source.annotate(ErrorType.INTERNAL_ERROR, "Unrecognised clause type " + source.getNodeType() + " (Internal error/TODO)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Reporter reporter, String indent, String incr) {
|
||||
reporter.note("DUMP", indent + "> TODO: Unrecognised clause (" + getSource().getNodeType() + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int resolveExpressions() {
|
||||
return 0;
|
||||
}
|
||||
}
|
94
slangc/model/clauses/Variables.sauce
Normal file
94
slangc/model/clauses/Variables.sauce
Normal file
@@ -0,0 +1,94 @@
|
||||
package slangc.model.clauses;
|
||||
|
||||
import slang.data.List;
|
||||
import slangc.api.Reporter;
|
||||
import slangc.model.InnerTypeScope;
|
||||
import slangc.model.LocalVariableSlot;
|
||||
import slangc.model.MemberModel;
|
||||
import slangc.model.Named;
|
||||
import slangc.model.StatementOwner;
|
||||
import slangc.model.TypeModel;
|
||||
import slangc.parser.Branch;
|
||||
import slangc.parser.Node;
|
||||
import slangc.parser.NodeType;
|
||||
|
||||
public class Variables extends ExpressionsOrVariables implements StatementOwner {
|
||||
private TypeModel type;
|
||||
private List<LocalVariableSlot> slots = new List<LocalVariableSlot>();
|
||||
|
||||
public Variables(StatementOwner owner, Branch source) {
|
||||
super(owner, source);
|
||||
|
||||
type = owner.resolveType(source.getSubnode(1));
|
||||
if (type == null)
|
||||
return;
|
||||
|
||||
Branch l = (Branch) source.getSubnode(2);
|
||||
if (l.getNodeType() == NodeType.NAME || l.getNodeType() == NodeType.INDEXED_NAME) {
|
||||
slots.append(new LocalVariableSlot(this, l, type));
|
||||
} else {
|
||||
for (int i = 0; i < l.countSubnodes(); i++) {
|
||||
slots.append(new LocalVariableSlot(this, (Branch)l.getSubnode(i), type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Reporter reporter, String indent, String incr) {
|
||||
reporter.note("DUMP", indent + "> Variables:");
|
||||
// do not recurse: type.dump(reporter, indent + incr, incr);
|
||||
for (int i = 0; i < slots.count(); i++) {
|
||||
slots.get(i).dump(reporter, indent + incr, incr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int resolveExpressions() {
|
||||
int nresolved = 0;
|
||||
for (int i = 0; i < slots.count(); i++) {
|
||||
nresolved += slots.get(i).resolveExpressions();
|
||||
}
|
||||
return nresolved;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public TypeModel resolveType(Node subnode) {
|
||||
return getOwner().resolveType(subnode);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public MemberModel getMethodOrField() {
|
||||
return getOwner().getMethodOrField();
|
||||
}
|
||||
|
||||
//@Override
|
||||
public InnerTypeScope getTypeScope() {
|
||||
return getOwner().getTypeScope();
|
||||
}
|
||||
|
||||
public boolean hasVariable(String name) {
|
||||
for (int i = 0; i < slots.count(); i++) {
|
||||
if (slots.get(i).getName().equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Named getVariable(String name) {
|
||||
for (int i = 0; i < slots.count(); i++) {
|
||||
if (slots.get(i).getName().equals(name)) {
|
||||
return slots.get(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int countVariables() {
|
||||
return slots.count();
|
||||
}
|
||||
|
||||
public LocalVariableSlot getVariable(int i) {
|
||||
return slots.get(i);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user