409 lines
9.7 KiB
C++
409 lines
9.7 KiB
C++
#pragma once
|
|
#include <stdlib.h>
|
|
#include "module.hpp"
|
|
|
|
#include "method.hpp"
|
|
#include "instruction.hpp"
|
|
|
|
namespace trainscript
|
|
{
|
|
class Block :
|
|
public Instruction
|
|
{
|
|
public:
|
|
ker::Vector<Instruction*> instructions;
|
|
|
|
~Block() {
|
|
for(auto *instr : instructions) delete instr;
|
|
}
|
|
|
|
bool validate(ExecutionContext &context, ker::String &errorCode) const {
|
|
errorCode = "";
|
|
for(auto *instr : instructions) {
|
|
if(instr->validate(context, errorCode) == false)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Variable execute(ExecutionContext &context) const override {
|
|
for(auto *instr : instructions) {
|
|
instr->execute(context);
|
|
}
|
|
return Variable::Void;
|
|
}
|
|
};
|
|
|
|
class ConstantExpression :
|
|
public Instruction
|
|
{
|
|
public:
|
|
Variable value;
|
|
ConstantExpression(const Variable &value) : value(value) { }
|
|
|
|
Variable execute(ExecutionContext &) const override {
|
|
return this->value;
|
|
}
|
|
|
|
Type expectedResult(ExecutionContext &) const override {
|
|
return this->value.type();
|
|
}
|
|
};
|
|
|
|
class VariableExpression :
|
|
public Instruction
|
|
{
|
|
public:
|
|
ker::String variableName;
|
|
VariableExpression(ker::String variableName) : variableName(variableName) { }
|
|
|
|
Variable execute(ExecutionContext &context) const override {
|
|
auto *var = context.get(this->variableName);
|
|
if(var == nullptr) {
|
|
die_extra("VariableExpression.VariableNotFound", this->variableName.str());
|
|
}
|
|
return *var;
|
|
}
|
|
|
|
bool validate(ExecutionContext &context, ker::String &errorCode) const override {
|
|
errorCode = "";
|
|
if(context.get(this->variableName) == nullptr) {
|
|
errorCode = "Variable " + this->variableName + " not found.";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Type expectedResult(ExecutionContext &context) const override {
|
|
Variable *var = context.get(this->variableName);
|
|
if(var == nullptr) {
|
|
return Type::Invalid;
|
|
} else {
|
|
return var->type();
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class VariableAssignmentExpression :
|
|
public Instruction
|
|
{
|
|
public:
|
|
ker::String variableName;
|
|
Instruction *expression;
|
|
VariableAssignmentExpression(ker::String variableName, Instruction *expression) :
|
|
variableName(variableName),
|
|
expression(expression)
|
|
{
|
|
|
|
}
|
|
|
|
Variable execute(ExecutionContext &context) const override {
|
|
if(this->expression == nullptr) {
|
|
die("VariableAssignmentExpression.ExpressionMissing");
|
|
}
|
|
Variable result = this->expression->execute(context);
|
|
|
|
Variable *target = context.get(this->variableName);
|
|
if(target == nullptr) {
|
|
die_extra("VariableAssignmentExpression.VariableNotFound", this->variableName.str());
|
|
}
|
|
|
|
if(target->type() != result.type()) {
|
|
die_extra("VariableAssignmentExpression.ExpectedType", result.type().name());
|
|
}
|
|
|
|
*target = result;
|
|
|
|
return *target;
|
|
}
|
|
|
|
bool validate(ExecutionContext &context, ker::String &errorCode) const override {
|
|
errorCode = "";
|
|
if(this->expression == nullptr) {
|
|
errorCode = "Missing expression.";
|
|
return false;
|
|
}
|
|
Type result = this->expression->expectedResult(context);
|
|
if(result == Type::Invalid) {
|
|
errorCode = "Expression returns invalid type.";
|
|
return false;
|
|
}
|
|
if(result == Type::Void) {
|
|
errorCode = "Void cannot be assigned to a variable";
|
|
return false;
|
|
}
|
|
Variable *var = context.get(this->variableName);
|
|
if(var == nullptr) {
|
|
errorCode = "Variable " + this->variableName + " not found.";
|
|
return false;
|
|
}
|
|
if(var->type() != result) {
|
|
errorCode = "Variable assignment has invalid type.";
|
|
return false;
|
|
}
|
|
if(this->expression->validate(context, errorCode) == false)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
Type expectedResult(ExecutionContext &context) const override {
|
|
if(this->expression == nullptr) {
|
|
return Type::Invalid;
|
|
} else {
|
|
return this->expression->expectedResult(context);
|
|
}
|
|
}
|
|
};
|
|
|
|
class MethodInvokeExpression :
|
|
public Instruction
|
|
{
|
|
public:
|
|
ker::String methodName;
|
|
ker::Vector<Instruction*> parameters;
|
|
|
|
MethodInvokeExpression(ker::String methodName) :
|
|
methodName(methodName)
|
|
{
|
|
|
|
}
|
|
|
|
Variable execute(ExecutionContext &context) const override
|
|
{
|
|
Method *method = context.module->method(this->methodName.str());
|
|
if(method == nullptr) {
|
|
die_extra("MethodInvokeExpression.MethodNotFound", this->methodName.str());
|
|
}
|
|
|
|
ker::Vector<Variable> vars(this->parameters.length());
|
|
vars.resize(this->parameters.length());
|
|
for(size_t i = 0; i < vars.length(); i++) {
|
|
vars[i].replace(this->parameters.at(i)->execute(context));
|
|
}
|
|
|
|
return method->invoke(vars);
|
|
}
|
|
|
|
bool validate(ExecutionContext &context, ker::String &errorCode) const override {
|
|
Method *method = context.module->method(this->methodName.str());
|
|
if(method == nullptr) {
|
|
errorCode = "The method " + this->methodName + " does not exist.";
|
|
return false;
|
|
}
|
|
|
|
ker::Vector<Type> arguments = method->arguments();
|
|
if(arguments.length() != this->parameters.length()) {
|
|
errorCode = "Argument count mismatch.";
|
|
return false;
|
|
}
|
|
for(size_t i = 0; i < arguments.length(); i++) {
|
|
if(this->parameters[i]->expectedResult(context) != arguments[i]) {
|
|
errorCode = "Argument type mismatch.";
|
|
return false;
|
|
}
|
|
if(this->parameters[i]->validate(context, errorCode) == false)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Type expectedResult(ExecutionContext &context) const override {
|
|
Method *method = context.module->method(this->methodName.str());
|
|
if(method == nullptr) {
|
|
return Type::Invalid;
|
|
}
|
|
return method->returnType();
|
|
}
|
|
};
|
|
|
|
class ArithmeticExpression :
|
|
public Instruction
|
|
{
|
|
public:
|
|
Operation op;
|
|
Instruction *lhs, *rhs;
|
|
|
|
ArithmeticExpression(Instruction *lhs, Instruction *rhs, Operation op) :
|
|
op(op),
|
|
lhs(lhs),
|
|
rhs(rhs)
|
|
{
|
|
|
|
}
|
|
|
|
Variable execute(ExecutionContext &context) const override {
|
|
if(this->lhs == nullptr) {
|
|
die_extra("ArithmeticExpression.ExpressionMissing", "Left-hand side");
|
|
}
|
|
if(this->rhs == nullptr) {
|
|
die_extra("ArithmeticExpression.ExpressionMissing", "Right-hand side");
|
|
}
|
|
|
|
Variable left = this->lhs->execute(context);
|
|
Variable right = this->rhs->execute(context);
|
|
|
|
if(left.type() != right.type()) {
|
|
die("ArithmeticExpression.TypeMismatch");
|
|
}
|
|
|
|
if(left.type().hasOperator(this->op) == false) {
|
|
die_extra("ArithmeticExpression.InvalidOperator", "The operator was not defined for this type.");
|
|
}
|
|
|
|
Variable result = left.type().apply(left, this->op, right);
|
|
|
|
if(result.type().usable() == false) {
|
|
die_extra("ArithmeticExpression.InvalidResult", result.type().name());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool validate(ExecutionContext &context, ker::String &errorCode) const override {
|
|
errorCode = "";
|
|
if(this->lhs == nullptr) {
|
|
errorCode = "Left part of operand is missing.";
|
|
return false;
|
|
}
|
|
if(this->rhs == nullptr) {
|
|
errorCode = "Right part of operand is missing.";
|
|
return false;
|
|
}
|
|
Type lhsType = this->lhs->expectedResult(context);
|
|
Type rhsType = this->rhs->expectedResult(context);
|
|
if(lhsType != rhsType) {
|
|
errorCode = "Types of operands do not match.";
|
|
return false;
|
|
}
|
|
if(lhsType == Type::Invalid) {
|
|
errorCode = "Invalid type can't be used for operand.";
|
|
return false;
|
|
}
|
|
if(rhsType == Type::Void) {
|
|
errorCode = "VOID type can't be used for operand.";
|
|
return false;
|
|
}
|
|
|
|
if(lhsType.hasOperator(this->op) == false) {
|
|
errorCode = "The operator is not defined for this type.";
|
|
return false;
|
|
}
|
|
|
|
if(this->lhs->validate(context, errorCode) == false)
|
|
return false;
|
|
if(this->rhs->validate(context, errorCode) == false)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
Type expectedResult(ExecutionContext &context) const override {
|
|
if(this->lhs == nullptr) {
|
|
return Type::Invalid;
|
|
} else {
|
|
return this->lhs->expectedResult(context);
|
|
}
|
|
}
|
|
};
|
|
|
|
class IfExpression :
|
|
public Instruction
|
|
{
|
|
public:
|
|
Instruction *condition;
|
|
Instruction *blockTrue;
|
|
Instruction *blockFalse;
|
|
|
|
IfExpression(Instruction *condition, Instruction *blockTrue, Instruction *blockFalse) :
|
|
condition(condition),
|
|
blockTrue(blockTrue),
|
|
blockFalse(blockFalse)
|
|
{
|
|
|
|
}
|
|
|
|
Variable execute(ExecutionContext &context) const override {
|
|
if(this->condition == nullptr) {
|
|
die("IfExpression.ConditionMissing");
|
|
}
|
|
|
|
Variable result = this->condition->execute(context);
|
|
if(result.type() != Type::Bool) {
|
|
die_extra("IfExpression.TypeMismatch", result.type().name());
|
|
}
|
|
if((result.value<Bool>() == true) && (this->blockTrue != nullptr)) {
|
|
this->blockTrue->execute(context);
|
|
}
|
|
if((result.value<Bool>() == false) && (this->blockFalse != nullptr)) {
|
|
this->blockFalse->execute(context);
|
|
}
|
|
return Variable::Void;
|
|
}
|
|
};
|
|
|
|
class RepeatEndlessExpression :
|
|
public Instruction
|
|
{
|
|
public:
|
|
Instruction *block;
|
|
|
|
RepeatEndlessExpression(Instruction *block) :
|
|
block(block)
|
|
{
|
|
|
|
}
|
|
|
|
Variable execute(ExecutionContext &context) const override {
|
|
if(this->block == nullptr) {
|
|
die("RepeatEndlessExpression.BlockMissing");
|
|
}
|
|
|
|
while(true)
|
|
{
|
|
Variable result = this->block->execute(context);
|
|
(void)result;
|
|
}
|
|
return Variable::Void;
|
|
}
|
|
};
|
|
|
|
|
|
class RepeatWhileExpression :
|
|
public Instruction
|
|
{
|
|
public:
|
|
Instruction *condition;
|
|
Instruction *block;
|
|
|
|
RepeatWhileExpression(Instruction *condition, Instruction *block) :
|
|
condition(condition),
|
|
block(block)
|
|
{
|
|
|
|
}
|
|
|
|
Variable execute(ExecutionContext &context) const override {
|
|
if(this->condition == nullptr) {
|
|
die("RepeatWhileExpression.ConditionMissing");
|
|
}
|
|
if(this->block == nullptr) {
|
|
die("RepeatWhileExpression.BlockMissing");
|
|
}
|
|
|
|
while(true)
|
|
{
|
|
Variable cond = this->condition->execute(context);
|
|
if(cond.type() != Type::Bool) {
|
|
return Variable::Invalid;
|
|
}
|
|
if(cond.value<Bool>() == false) {
|
|
break;
|
|
}
|
|
|
|
this->block->execute(context);
|
|
}
|
|
return Variable::Void;
|
|
}
|
|
};
|
|
}
|