148 lines
5.3 KiB
Java
148 lines
5.3 KiB
Java
package com.hypixel.hytale.server.npc.util.expression.compile;
|
|
|
|
import com.hypixel.hytale.server.npc.util.expression.ExecutionContext;
|
|
import com.hypixel.hytale.server.npc.util.expression.Expression;
|
|
import com.hypixel.hytale.server.npc.util.expression.Scope;
|
|
import com.hypixel.hytale.server.npc.util.expression.ValueType;
|
|
import com.hypixel.hytale.server.npc.util.expression.compile.ast.AST;
|
|
import com.hypixel.hytale.server.npc.util.expression.compile.ast.ASTOperand;
|
|
import com.hypixel.hytale.server.npc.util.expression.compile.ast.ASTOperator;
|
|
import com.hypixel.hytale.server.npc.util.expression.compile.ast.ASTOperatorFunctionCall;
|
|
import com.hypixel.hytale.server.npc.util.expression.compile.ast.ASTOperatorTuple;
|
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
import java.text.ParseException;
|
|
import java.util.List;
|
|
import java.util.Stack;
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
|
|
public class CompileContext implements Parser.ParsedTokenConsumer {
|
|
private final Parser parser = new Parser(Expression.getLexerInstance());
|
|
private final Stack<AST> operandStack = new Stack<>();
|
|
@Nonnull
|
|
private final ExecutionContext executionContext;
|
|
private Scope scope;
|
|
private List<ExecutionContext.Instruction> instructions;
|
|
private ValueType resultType = ValueType.VOID;
|
|
|
|
public CompileContext() {
|
|
this.executionContext = new ExecutionContext();
|
|
}
|
|
|
|
public CompileContext(Scope scope) {
|
|
this();
|
|
this.scope = scope;
|
|
}
|
|
|
|
public Scope getScope() {
|
|
return this.scope;
|
|
}
|
|
|
|
@Nonnull
|
|
public Stack<AST> getOperandStack() {
|
|
return this.operandStack;
|
|
}
|
|
|
|
@Nonnull
|
|
public ExecutionContext getExecutionContext() {
|
|
return this.executionContext;
|
|
}
|
|
|
|
public ValueType compile(@Nonnull String expression, Scope compileScope, boolean fullResolve) {
|
|
return this.compile0(expression, compileScope, fullResolve, null);
|
|
}
|
|
|
|
public ValueType compile(@Nonnull String expression, Scope compileScope, boolean fullResolve, List<ExecutionContext.Instruction> instructions) {
|
|
ValueType valueType = this.compile0(expression, compileScope, fullResolve, instructions);
|
|
this.setInstructions(null);
|
|
return valueType;
|
|
}
|
|
|
|
protected ValueType compile0(@Nonnull String expression, Scope compileScope, boolean fullResolve, List<ExecutionContext.Instruction> instructions) {
|
|
Scope saveScope = this.scope;
|
|
Scope oldScope = this.executionContext.setScope(compileScope);
|
|
this.scope = compileScope;
|
|
this.setInstructions(instructions);
|
|
this.compile(expression, fullResolve);
|
|
this.executionContext.setScope(oldScope);
|
|
this.scope = saveScope;
|
|
return this.resultType;
|
|
}
|
|
|
|
public ValueType compile(@Nonnull String expression, boolean fullResolve) {
|
|
try {
|
|
this.operandStack.clear();
|
|
this.resultType = ValueType.VOID;
|
|
if (this.instructions == null) {
|
|
this.instructions = new ObjectArrayList();
|
|
}
|
|
|
|
this.instructions.clear();
|
|
this.parser.parse(expression, this);
|
|
this.resultType = this.operandStack.getFirst().genCode(this.instructions, fullResolve ? this.scope : null);
|
|
return this.resultType;
|
|
} catch (Throwable var4) {
|
|
throw new IllegalStateException("Error compiling expression '" + expression + "': " + var4.getMessage(), var4);
|
|
}
|
|
}
|
|
|
|
public List<ExecutionContext.Instruction> getInstructions() {
|
|
return this.instructions;
|
|
}
|
|
|
|
public void setInstructions(List<ExecutionContext.Instruction> instructionList) {
|
|
this.instructions = instructionList;
|
|
}
|
|
|
|
public ValueType getResultType() {
|
|
return this.resultType;
|
|
}
|
|
|
|
@Nullable
|
|
public ExecutionContext.Operand getAsOperand() {
|
|
if (this.operandStack.size() != 1) {
|
|
throw new IllegalArgumentException("There must be 1 element on stack to get as operand");
|
|
} else {
|
|
AST ast = this.operandStack.getFirst();
|
|
return ast.isConstant() ? ast.asOperand() : null;
|
|
}
|
|
}
|
|
|
|
public void checkResultType(ValueType type) {
|
|
if (type == ValueType.VOID) {
|
|
throw new IllegalArgumentException("Result type can't be void");
|
|
} else if (this.resultType == ValueType.VOID) {
|
|
throw new IllegalArgumentException("Compiled expression result type can't be void");
|
|
} else if (type != this.resultType) {
|
|
throw new IllegalStateException("Result type expected is " + type + " but got " + this.resultType);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void pushOperand(@Nonnull Parser.ParsedToken parsedToken) {
|
|
this.operandStack.push(ASTOperand.createFromParsedToken(parsedToken, this));
|
|
}
|
|
|
|
@Override
|
|
public void processOperator(@Nonnull Parser.ParsedToken operator) throws ParseException {
|
|
ASTOperator.fromParsedOperator(operator, this);
|
|
}
|
|
|
|
@Override
|
|
public void processFunction(int argumentCount) throws ParseException {
|
|
ASTOperatorFunctionCall.fromParsedFunction(argumentCount, this);
|
|
}
|
|
|
|
@Override
|
|
public void processTuple(@Nonnull Parser.ParsedToken openingToken, int argumentCount) {
|
|
ASTOperatorTuple.fromParsedTuple(openingToken, argumentCount, this);
|
|
}
|
|
|
|
@Override
|
|
public void done() {
|
|
if (this.operandStack.size() != 1) {
|
|
throw new IllegalStateException("Need exactly one returned value in expression");
|
|
}
|
|
}
|
|
}
|