DMS/DMS/dms_state_interpret.cpp

665 lines
18 KiB
C++

#include "pch.h"
#include "dms_state.h"
#include "utils.h"
#include "Handlers.h"
using namespace dms::utils;
using namespace dms::exceptions;
using namespace dms::codes;
namespace dms {
// This is a pointer to a choicehandler! dms_state and utils have codepedencies, so I have to do this.
void dms_state::setHandler(Handler* hand) {
this->handler = hand;
}
enviroment* dms_state::getEnvironment(std::string env) {
if (environments.count(env)) {
return environments[env];
}
else {
if (blockExists(env)) {
enviroment* e = new enviroment;
if (run(env, &e->values)) {
std::cout << "Ran :D" << std::endl;
}
environments.insert_or_assign(env, e);
handler->OnEnviromentCreated(this, e);
return e;
}
else {
push_error(errors::error{ errors::non_existing_block ,utils::concat("Attempted to index a non existing enviroment block!") });
return nullptr;
}
}
return nullptr;
}
void checkCharacter(character* cha, std::string index, datatypes type) {
auto val = cha->values[index];
// If a type mismatch is present, overwrite them with the defaults
if (val.type != type) {
if (type == datatypes::string)
cha->values[index] = value("");
else if (type == datatypes::boolean) {
cha->values[index] = value(false);
}
else if (type == datatypes::number) {
cha->values[index] = value(0);
}
}
}
character* dms_state::getCharacter(std::string cha) {
if (characters.count(cha)) {
characters[cha]->seen = true;
return characters[cha];
}
else {
if (blockExists(cha)) {
pushMem();
character* cc = new character;
cc->fullname = isEnabled("fullname");
/*cc->set("fname", cha);
cc->set("lname", "");
cc->set("unknown", "Unknown");
cc->set("known", false);*/
if (isEnabled("omniscient")) {
cc->seen = true;
}
else {
cc->seen = false;
}
if (run(cha,&cc->values)) {
cc->values = *getMem();
checkCharacter(cc, "fname",datatypes::string);
if (cc->get("fname") == value("")) {
cc->set("fname",value(cha));
}
checkCharacter(cc, "unknown", datatypes::string);
checkCharacter(cc, "known", datatypes::boolean);
if (cc->get("known").b) {
cc->seen = true;
}
}
else {
popMem();
return nullptr;
}
characters.insert_or_assign(cha, cc);
// Call Character event!
handler->OnSpeakerCreated(this,cc);
popMem();
return cc;
}
else {
push_error(errors::error{ errors::non_existing_block ,utils::concat("Attempted to index a non existing character block!") });
return nullptr;
}
}
return nullptr;
}
size_t dms_state::seek(std::string label, std::vector<cmd*> cmds, codes::op code, size_t pos) {
for (size_t i = pos; i < cmds.size(); i++) {
if (cmds[i]->opcode == code && cmds[i]->args.args[0].getPrintable() == label)
return i;
}
return 0;
}
void dms_state::init(chunk* chunk, size_t& pos, size_t& max, std::vector<cmd*>& cmds) {
pos = 0;
max = chunk->cmds.size();
cmds = chunk->cmds;
}
// Instance, multiple instances can allow different code to work side by side
bool dms_state::run(std::string instance) {
//codes::op code;
//Spawn thread and run
return true;
}
bool dms_state::error(std::string err)
{
push_error(errors::error{ errors::unknown ,err });
return false;
}
bool dms_state::run() {
if (stop)
return false;
if (chunks[entry] == NULL) {
push_error(errors::error{ errors::non_existing_block ,utils::concat("Attempted to Jump to a non existing block [",entry,"]") });
return false;
}
return run("$INIT",getMem());
}
bool dms_state::run(std::string ent, memory* mem) {
if (stop) {
exitcode = 1;
return false;
}
if (chunks[ent] == NULL) {
push_error(errors::error{ errors::non_existing_block ,utils::concat("Attempted to Jump to a non existing block [",ent,"]") });
return false;
}
pushStateData();
pushMem(mem);
init(chunks[ent], n_pos, n_max, n_cmds);
bool working = true;
while (working) {
working = next(mem);
}
popMem();
popStateData();
return true;
}
bool dms_state::next(memory* mem) {
if (n_halt || stop)
return false;
if (!n_cmds.size())
init(chunks["$INIT"], n_pos, n_max, n_cmds);
n_c = n_cmds[n_pos++];
n_code = n_c->opcode;
//debug("(",n_pos,")> ",*n_c);
//wait();
switch (n_code)
{
// Handle flags here
case ENTR:
// When reading code from a file the flags are compiled and we need to load them
entry = n_c->args.args[0].getPrintable();
break;
case ENAB:
enable(n_c->args.args[0].getPrintable());
break;
case DISA:
disable(n_c->args.args[0].getPrintable());
break;
case codes::FILE:
cur_file = n_c->args.args[0].getPrintable();
case LOAD:
// Nothing needs to be done here
break;
case VERN:
Sversion = n_c->args.args[0].n;
// Version X.xx
// X: Will not guarantee compatibality. Code breaking changes should be expected especially on lower versions
// xx: Will not break compatibality. They may add features as it goes up the ranks, but those changes do not break older code. If they do they will increase the X part and not the xx part
if (Sversion > Iversion) {
push_error(errors::error{ errors::incompatible_version ,"This script was made for a later version of this interperter!" });
return false;
}
else if ((int)Iversion > (int)(Sversion)) {
push_warning(errors::error{ errors::incompatible_version ,"This script was made for an older version of this interperter, some features might not work!" });
}
break;
case USIN:
// How we add modules into the code. This is the code that actually loads that data!
break;
// Flags handled
case EXIT:
if (n_c->args.args.size()) {
exitcode = (int)n_c->args.args[0].n;
}
return true;
break;
case KNOT:
{
value cmp = n_c->args.args[0];
if (cmp.resolve(this).type == datatypes::boolean || cmp.resolve(this).isNil()) {
if (!cmp.resolve(this).b || cmp.resolve(this).isNil()) {
if (!assign(cmp, value(true))) {
return false;
}
}
else {
if (!assign(cmp, value(false))) {
return false;
}
}
}
else {
if (!assign(cmp, value(false))) {
return false;
}
}
break;
}
case IFFF:
{
value cmp = n_c->args.args[0].resolve(this);
value gt = n_c->args.args[1].resolve(this);
if (cmp.type == datatypes::boolean || cmp.isNil()) {
if (!cmp.b || cmp.isNil()) {
n_pos = seek(gt.getPrintable(), n_cmds, LABL, n_pos);
}
}
break;
}
case GOTO:
{
value labl = n_c->args.args[0];
if (isEnabled("forwardlabels")) {
size_t nnpos = seek(labl.resolve(this).getPrintable(), n_cmds, LABL, n_pos); // Seek from next pos (pos++) to end of cmds
if (!nnpos) {
// This could be a method, it could, but it isn't. We jump to searching from the beginning of the block
goto seek_from_1;
}
else {
n_pos = nnpos;
}
break;
}
seek_from_1:
size_t nnpos = seek(labl.resolve(this).getPrintable(), n_cmds, LABL, 0);
if (!nnpos) {
push_error(errors::error{ errors::choice_unknown ,utils::concat("Unknown label '",labl.resolve(this).getPrintable(),"'!") });
return false;
}
else {
n_pos = nnpos;
}
}
break;
case RETN:
{
value ret = n_c->args.args[0].resolve(this);
//c->args.args[0]->nuke(); // Lets clean this up
return_stack.push(ret);
return true; // We did it, let's return this
}
break;
case OFUN:
{
if (n_c->args.args[0].resolve(this).type == datatypes::custom) {
auto cust = n_c->args.args[0].resolve(this);
auto fname = n_c->args.args[1].getString();
auto assn = n_c->args.args[2];
dms_args args;
for (int i = 3; i < n_c->args.args.size(); i++) {
args.push(n_c->args.args[i]);
}
if (inv_map.count(cust.ctype)) {
Invoker* inv = inv_map[cust.ctype];
auto ret = inv->Invoke(fname, cust.c, this, &args);
if (assn.type != datatypes::nil) {
if (!assign(assn, ret)) {
return false;
}
}
}
else {
error("Custom object not assoiated with any functions!");
}
}
else {
std::string obj = n_c->args.args[0].getPrintable();
if (obj == "nil") {
obj = n_c->args.args[0].getPrintable();
}
std::string funcname = n_c->args.args[1].getPrintable();
value assn = n_c->args.args[2];
dms_args args;
for (int i = 3; i < n_c->args.args.size(); i++) {
args.push(n_c->args.args[i]);
}
value ret;
if (characterExists(obj)) {
ret = getCharacter(obj)->Invoke(funcname, this, &args);
}
else if (environmentExists(obj)) {
ret = getEnvironment(obj)->Invoke(funcname, this, &args);
}
if (ret.type == datatypes::error)
return error(ret.getString());
if (assn.type != datatypes::nil) {
if (!assign(assn, ret)) {
return false;
}
}
}
}
break;
case FUNC:
{
std::string funcname = n_c->args.args[0].getPrintable();
value assn = n_c->args.args[1];
dms_args args;
value ret;
for (int i = 2; i < n_c->args.args.size(); i++) {
args.push(n_c->args.args[i]);
}
// If we have a block made function we don't invoke like normal
if (functionExists(funcname)) {
call_stack.push(funcname);
ret = invoker.Invoke("$BlockInvoke$", this, &args);
}
else {
ret = invoker.Invoke(funcname, this, &args);
}
if (ret.type == datatypes::error) {
return error(ret.getString());
}
if (assn.type != datatypes::nil) {
if (!assign(assn, ret)) {
return false;
}
}
}
break;
case ASID:
{
value env = n_c->args.args[1];
value indx = n_c->args.args[2].resolve(this);
value assn = n_c->args.args[3].resolve(this);
if (env.type == datatypes::block && blockExists(env.getString())) { // If this is a block let's handle this
enviroment* e = nullptr;
if (environments.count(env.getString())) {
e = environments[env.getString()];
}
else if (characters.count(env.getString())) {
e = characters[env.getString()];
}
e->values[indx.getString()] = assn;
}
else if (env.type == datatypes::env) {
if (indx.type == datatypes::number) {
env.e->pushValue(assn);
}
else {
push_error(errors::error{ errors::invalid_type ,concat("Expected a number value got ",datatype[indx.type]) });
return false;
}
}
else if (env.type == datatypes::custom) {
//env.c->NewIndex(indx, assn);
//if(!assign( assn, env->c->Index(indx));
// Call the method within the custom data
}
break;
}
case ADD:
{
value assn = n_c->args.args[0];
value o1 = n_c->args.args[1];
value o2 = n_c->args.args[2];
value ret = o1.resolve(this) + o2.resolve(this);
if (!assign(assn, ret)) {
return false;
}
}
break;
case SUB:
{
value assn = n_c->args.args[0];
value o1 = n_c->args.args[1];
value o2 = n_c->args.args[2];
value ret = o1.resolve(this) - o2.resolve(this);
if (!assign(assn, ret)) {
return false;
}
}
break;
case MUL:
{
value assn = n_c->args.args[0];
value o1 = n_c->args.args[1];
value o2 = n_c->args.args[2];
value ret = o1.resolve(this) * o2.resolve(this);
if (!assign(assn, ret)) {
return false;
}
}
break;
case DIV:
{
value assn = n_c->args.args[0];
value o1 = n_c->args.args[1];
value o2 = n_c->args.args[2];
value ret = o1.resolve(this) / o2.resolve(this);
if (!assign(assn, ret)) {
return false;
}
}
break;
case POW:
{
value assn = n_c->args.args[0];
value o1 = n_c->args.args[1];
value o2 = n_c->args.args[2];
value ret = pow(o1.resolve(this).n, o2.resolve(this).n);
if (!assign(assn, ret)) {
return false;
}
}
break;
case MOD:
{
value assn = n_c->args.args[0];
value o1 = n_c->args.args[1];
value o2 = n_c->args.args[2];
value ret = std::fmod(o1.resolve(this).n, o2.resolve(this).n);
if (!assign(assn, ret)) {
return false;
}
}
break;
case INDX:
{
value assn = n_c->args.args[0];
value env = n_c->args.args[1];
value indx = n_c->args.args[2].resolve(this);
if (env.type == datatypes::block && blockExists(env.getString())) { // If this is a block let's handle this
enviroment* e = nullptr;
if (environments.count(env.getString())) {
e = environments[env.getString()];
}
else if (characters.count(env.getString())) {
e = characters[env.getString()];
}
if (!assign(assn, e->values[indx.getPrintable()])) {
return false;
}
}
else if (env.type == datatypes::env) {
if (indx.type == datatypes::number) {
if (!assign(assn, env.e->getValue(indx))) {
return false;
}
}
else {
push_error(errors::error{ errors::invalid_type ,concat("Expected a number value got ",datatype[indx.type]) });
return false;
}
}
else if (env.type == datatypes::custom) {
//if(!assign( assn, env.c->Index(indx))) {
// return false;
//}
// Call the method within the custom data
}
}
break;
case LIST:
//We need to create an enviroment value then set that
{
dms_list* env = new dms_list;
// Inject the size of the list
env->hpart["$size"] = n_c->args.args[1];
value val = new value;
val.set(env);
if (!assign(n_c->args.args[0], val)) {
return false;
}
}
break;
case INST:
{
value list = (*mem)[n_c->args.args[0].getPrintable()];
list.e->pushValue(n_c->args.args[1]);
}
break;
case COMP:
{
comp cmp = (comp)n_c->args.args[0].i;
value assn = n_c->args.args[1];
value left = n_c->args.args[2].resolve(this);
value right = n_c->args.args[3].resolve(this);
switch (cmp) {
case comp::eq: {
if (!assign(assn, left == right)) {
return false;
}
break;
}
case comp::gt: {
if (left.isNil() || right.isNil()) { push_error(errors::error{ errors::unknown ,"Attempt to compare a nil value!" }); return false; }
if (!assign(assn, left > right)) {
return false;
}
break;
}
case comp::gteq: {
if (left.isNil() || right.isNil()) { push_error(errors::error{ errors::unknown ,"Attempt to compare a nil value!" }); return false; }
if (!assign(assn, left >= right)) {
return false;
}
break;
}
case comp::lt: {
if (left.isNil() || right.isNil()) { push_error(errors::error{ errors::unknown ,"Attempt to compare a nil value!" }); return false; }
if (!assign(assn, left < right)) {
return false;
}
break;
}
case comp::lteq: {
if (left.isNil() || right.isNil()) { push_error(errors::error{ errors::unknown ,"Attempt to compare a nil value!" }); return false; }
if (!assign(assn, left <= right)) {
return false;
}
break;
}
case comp::nteq: {
if (!assign(assn, left != right)) {
return false;
}
break;
}
}
}
break;
case HALT:
//wait();
//sleep(700);
std::cout << std::endl;
//HandleHalt.fire(this); // We need a way to keep things running while also stopping the state from running. Shouldn't be too bad
break;
case WAIT:
sleep((int)(n_c->args.args[0].n * 1000));
break;
case DSPD:
if (speaker == nullptr) {
push_error(errors::error{ errors::unknown ,utils::concat("A call to set speaker speed, but a speaker has not been defined!") });
return false;
}
else {
speaker->spd = n_c->args.args[0].n;
}
break;
case SSPK:
//Because we are using void* we must cast our pointers
if (characterExists(n_c->args.args[0].getString())) {
speaker = getCharacter(n_c->args.args[0].getString());
if (!handler->handleSpeaker(this, speaker))
return false;
}
else {
push_error(errors::error{ errors::disp_unknown,concat("Unknown character '",n_c->args.args[0].getString(),"'!") });
return false;
}
break;
case APND:
OnAppendText.fire(message{
speaker,
n_c->args.args[0].resolve(this).getPrintable()
});
//if (!handler->handleMessageAppend(this, c->args.args[0].resolve(this).getPrintable()))
// return false;
break;
case CHAR:
{
std::string cha = n_c->args.args[0].getString();
getCharacter(cha);
break;
}
case DISP:
{
OnText.fire(message{
speaker,
n_c->args.args[0].resolve(this).getPrintable()
});
/*if (!handler->handleMessageDisplay(this, c->args.args[0].resolve(this).getPrintable()))
return false;*/
}
break;
case ASGN:
if (!assign(n_c->args.args[0], n_c->args.args[1])) {
return false;
}
break;
case LINE:
cur_line = (size_t)n_c->args.args[0].i;
break;
case NOOP:
break;
case CHOI:
//Because we are using void* we must cast our pointers
{
std::vector<std::string> args;
std::string prompt = n_c->args.args[0].getString();
std::string fn = n_c->args.args[1].getString();
for (size_t i = 2; i < n_c->args.args.size(); i++)
args.push_back(n_c->args.args[i].resolve(this).getString());
size_t npos = handler->handleChoice(this, prompt, args);
size_t nnpos = seek(concat("CHOI_", fn, "_", npos), n_cmds, LABL, n_pos);
if (!nnpos) {
push_error(errors::error{ errors::choice_unknown ,utils::concat("Unknown choice!") });
return false;
}
else {
n_pos = nnpos;
}
}
break;
case JUMP:
// Value assert resolves the data so a variable must eventually equal a string
if (utils::valueassert(n_c->args, this, datatypes::string)) {
std::string block = n_c->args.args[0].resolve(this).getString();
if (chunks[block] == NULL) {
push_error(errors::error{ errors::non_existing_block ,utils::concat("Attempted to Jump to a non existing block [",block,"]") });
return false;
}
else if (chunks[block]->type != bt_block) {
push_error(errors::error{ errors::non_existing_block ,utils::concat("Attempted to Jump to a non standard block [",block,"] type: ",getBlockType(chunks[block]->type)) });
return false;
}
else {
init(chunks[block], n_pos, n_max, n_cmds);
}
}
else {
//utils::print(c->args.args[0].type);
datatypes set = n_c->args.args[0].resolve(this).type;
//utils::print("> ",set);
push_error(errors::error{ errors::invalid_arguments, utils::concat("String expected got ",datatype[set]), true, n_ln });
return false;
}
break;
default:
break;
}
if (n_pos == n_max) {
// How did we get here? The end of a block?
return false;
}
return true;
}
}