diff --git a/DMS/DMS.cpp b/DMS/DMS.cpp index 61024c5..ee99ff3 100644 --- a/DMS/DMS.cpp +++ b/DMS/DMS.cpp @@ -1,8 +1,32 @@ #include "dms.h" +#include "Handlers.h" +#include "utils.h" +#include using namespace dms; +// You can use your own choice handler! +class myChoi : public choiceHandler { + uint8_t manageChoice(dms_state* state, dms_args choices) const override { + uint8_t count = choices.args.size(); + uint8_t pos; + std::string prompt = choices.args[0]->s->getValue(); + for (size_t i = 1; i < count; i++) + std::cout << i << ": " << choices.args[i]->s->getValue() << std::endl; + std::cout << prompt; + std::cin >> pos; + return pos; + } +}; int main() { LineParser parser = LineParser("test.dms"); dms_state* state = parser.Parse(); + try { + //state->setChoiceHandler(new myChoi); + } + catch (std::exception& e) { + std::cout << e.what() << '\n'; + return -1; + } state->dump(); + state->run(); } \ No newline at end of file diff --git a/DMS/DMS.vcxproj b/DMS/DMS.vcxproj index ecd5f34..40ae047 100644 --- a/DMS/DMS.vcxproj +++ b/DMS/DMS.vcxproj @@ -142,6 +142,7 @@ + @@ -159,6 +160,7 @@ + diff --git a/DMS/DMS.vcxproj.filters b/DMS/DMS.vcxproj.filters index 9fa0696..d4ba9eb 100644 --- a/DMS/DMS.vcxproj.filters +++ b/DMS/DMS.vcxproj.filters @@ -66,6 +66,9 @@ Source Files\DMS + + Source Files + @@ -107,5 +110,8 @@ Header Files\DMS + + Header Files + \ No newline at end of file diff --git a/DMS/Handlers.cpp b/DMS/Handlers.cpp new file mode 100644 index 0000000..5d3550b --- /dev/null +++ b/DMS/Handlers.cpp @@ -0,0 +1,13 @@ +#include "Handlers.h" +namespace dms { + uint8_t choiceHandler::manageChoice(dms_state* state, dms_args choices) const { + uint8_t count = choices.args.size(); + std::string pos; + std::string prompt = choices.args[0]->s->getValue(); + for (size_t i = 1; i < count; i++) + std::cout << i << ": " << choices.args[i]->s->getValue() << std::endl; + std::cout << prompt << " "; + std::cin >> pos; + return std::stoi(pos)-1; + } +} \ No newline at end of file diff --git a/DMS/Handlers.h b/DMS/Handlers.h new file mode 100644 index 0000000..1908832 --- /dev/null +++ b/DMS/Handlers.h @@ -0,0 +1,10 @@ +#pragma once +#include "value.h" +#include "utils.h" +#include +namespace dms { + struct choiceHandler + { + virtual uint8_t manageChoice(dms_state* state, dms_args choices) const; + }; +} diff --git a/DMS/LineParserMatchProcess.cpp b/DMS/LineParserMatchProcess.cpp index 0b214b1..7336930 100644 --- a/DMS/LineParserMatchProcess.cpp +++ b/DMS/LineParserMatchProcess.cpp @@ -246,7 +246,6 @@ namespace dms { c->opcode = codes::ASGN; c->args.push(var); value* ref = buildVariable(); - print(stream->peek()); if (match_process_standard(stream,ref)) { c->args.push(ref); current_chunk->addCmd(c); diff --git a/DMS/LineParserParse.cpp b/DMS/LineParserParse.cpp index e41306e..12f1b22 100644 --- a/DMS/LineParserParse.cpp +++ b/DMS/LineParserParse.cpp @@ -331,7 +331,15 @@ namespace dms { token current = stream->next(); createBlock("$INIT", blocktype::bt_block); cmd* flagcmd = new cmd; + size_t current_line = 0; while (stream->peek().type != tokens::eof) { + if (stream->peek().line_num != current_line) { + current_line = stream->peek().line_num; + cmd* ln = new cmd; + ln->opcode = codes::LINE; + ln->args.push(buildValue((int)current_line+1)); + current_chunk->addCmd(ln); + } if (current.type == tokens::flag) { temp = stream->next(tokens::newline); stream->prev(); // Unconsume the newline piece diff --git a/DMS/LineParserUtils.cpp b/DMS/LineParserUtils.cpp index ffacf4c..e41271a 100644 --- a/DMS/LineParserUtils.cpp +++ b/DMS/LineParserUtils.cpp @@ -228,7 +228,7 @@ namespace dms { else if (bk_name == "$END") { cmd* c = new cmd; c->opcode = codes::JUMP; - c->args.push(buildVariable("$END")); + c->args.push(buildValue("$END")); current_chunk->addCmd(c); state->push_chunk(current_chunk->name, current_chunk); current_chunk = state->chunks["$END"]; @@ -245,16 +245,16 @@ namespace dms { if (state->isEnabled("leaking") && (current_chunk != nullptr && current_chunk->name != "$INIT")) { cmd* c = new cmd; c->opcode = codes::JUMP; - c->args.push(buildVariable(bk_name)); + c->args.push(buildValue(bk_name)); current_chunk->addCmd(c); } if (current_chunk!= nullptr && current_chunk->name == "$INIT") { cmd* c = new cmd; c->opcode = codes::JUMP; if(state->entry!="$undefined") - c->args.push(buildVariable(state->entry)); + c->args.push(buildValue(state->entry)); else - c->args.push(buildVariable(bk_name)); + c->args.push(buildValue(bk_name)); current_chunk->addCmd(c); } if (current_chunk != nullptr && current_chunk->name == "$END") { @@ -263,7 +263,7 @@ namespace dms { if (state->entry != "$undefined") c->args.push(buildValue(0)); else - c->args.push(buildVariable(bk_name)); + c->args.push(buildValue(bk_name)); current_chunk->addCmd(c); } current_chunk = new chunk; diff --git a/DMS/codes.cpp b/DMS/codes.cpp index cd005dc..8895a8d 100644 --- a/DMS/codes.cpp +++ b/DMS/codes.cpp @@ -43,5 +43,6 @@ const std::string dms::codes::list[] = { "DIV", "POW", "MOD", - "LIST" + "LIST", + "LINE" }; \ No newline at end of file diff --git a/DMS/codes.h b/DMS/codes.h index 4631b1b..7353057 100644 --- a/DMS/codes.h +++ b/DMS/codes.h @@ -46,6 +46,7 @@ namespace dms::codes { POW, MOD, LIST, + LINE }; extern const std::string list[]; static bool isControl(const op code) { diff --git a/DMS/dms_exceptions.h b/DMS/dms_exceptions.h index f5f4b7e..8a5d802 100644 --- a/DMS/dms_exceptions.h +++ b/DMS/dms_exceptions.h @@ -1,10 +1,15 @@ #pragma once #include namespace dms::exceptions { - struct StringBoundsExeception : public std::exception { + struct StringBoundsException : public std::exception { const char* what() const throw () { return "Attempt to sub outside of the bounds of the string!"; } }; + struct BadChoiceHandlerException : public std::exception { + const char* what() const throw () { + return "ChoiceHandler Expected got ???"; + } + }; } diff --git a/DMS/dms_state.cpp b/DMS/dms_state.cpp index e37226d..5d9e6d4 100644 --- a/DMS/dms_state.cpp +++ b/DMS/dms_state.cpp @@ -1,4 +1,5 @@ #include "dms_state.h" +#include "Handlers.h" namespace dms { dms_state::dms_state() { // We should define the defaults for the enables @@ -15,6 +16,7 @@ namespace dms { cc->args.push(buildValue(0)); c->addCmd(cc); push_chunk("$END", c); + setChoiceHandler(new choiceHandler); // Use the default implementation } void dms_state::enable(std::string flag) { enables[flag] = true; diff --git a/DMS/dms_state.h b/DMS/dms_state.h index 4b998a2..d839805 100644 --- a/DMS/dms_state.h +++ b/DMS/dms_state.h @@ -1,18 +1,26 @@ #pragma once #include "errors.h" #include "chunk.h" +#include "dms_exceptions.h" #include #include #include #include #include +// Threading Stuff +#include +#include +#include namespace dms { struct dms_state { + void* choi = nullptr; std::unordered_map memory; std::map chunks; std::string entry = "$undefined"; std::map enables; + const double Iversion = 1.0; + double Cversion; // The version of errors::error err; bool stop = false; @@ -22,10 +30,16 @@ namespace dms { void push_error(errors::error err); void push_warning(errors::error err); void push_chunk(std::string s, chunk* c); - bool run(std::string instance = "Main"); + bool run(std::string instance); double version=1.0; void enable(std::string flag); void disable(std::string flag); bool isEnabled(std::string flag); + void setChoiceHandler(void* choi); + bool run(); + private: + // From what I gathered + //std::mutex memory_mutex; + void init(chunk* chunk, size_t &pos,size_t &max, std::vector& cmds); }; } diff --git a/DMS/dms_state_interpret.cpp b/DMS/dms_state_interpret.cpp index 54e05f4..493e985 100644 --- a/DMS/dms_state_interpret.cpp +++ b/DMS/dms_state_interpret.cpp @@ -1,18 +1,110 @@ #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::setChoiceHandler(void* choi) { + this->choi = choi; + } + void dms_state::init(chunk* chunk, size_t& pos, size_t& max, std::vector& 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; - //TODO: parse the cmds and do stuff - - /*switch (code) - { - case codes::NOOP: - - break; - default: - break; - }*/ + //Spawn thread and run return true; - } + } + bool dms_state::run() { + codes::op code; + cmd* c = nullptr; + bool halt = false; + size_t pos=0; + size_t max = 0; + std::vector cmds; + 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; + } + init(chunks["$INIT"],pos,max,cmds); + + //TODO: parse the cmds and do stuff + // If this is running in a thread then stop will force this loop to stop + size_t ln = 0; + while (!stop || !halt) { + c = cmds[pos++]; + code = c->opcode; + utils::print("> ",*c); + //utils::wait(); + switch (code) + { + // Handle flags here + case ENTR: + // When reading code from a file the flags are compiled and we need to load them + entry = c->args.args[0]->s->getValue(); + break; + case ENAB: + enable(c->args.args[0]->s->getValue()); + break; + case DISA: + disable(c->args.args[0]->s->getValue()); + break; + case LOAD: + // Nothing needs to be done here + break; + case VERN: + Cversion = c->args.args[0]->n->getValue(); + break; + case USIN: + // How we add modules into the code. This is the code that actually loads that data! + break; + // Flags handled + case LINE: + ln = c->args.args[0]->n->getValue(); + break; + case NOOP: + break; + case CHOI: + if (utils::valueassertall(c->args, memory, datatypes::string)) { + //Because we are using void* we must cast our pointers + //The implementation of this however should mean that you only ever have to deal with one "ChoiceHandler. One annoying void*" + pos += 2*(*(choiceHandler*)choi).manageChoice(this, c->args); + } + break; + case JUMP: + value* v1; + // Value assert resolves the data so a variable must eventually equal a string + if (utils::valueassert(c->args, memory, datatypes::string)) { + std::string block = c->args.args[0]->resolve(memory)->s->getValue(); + 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 { + init(chunks[block], pos, max, cmds); + } + } + else { + datatypes set = c->args.args[0]->resolve(memory)->type; + push_error(errors::error{ errors::invalid_arguments, utils::concat("String expected got ",set), true,ln }); + return false; + } + break; + default: + break; + } + if (pos == max) { + // How did we get here? The end of a block? + break; + } + } + END: + + return true; + } } \ No newline at end of file diff --git a/DMS/dump.bin b/DMS/dump.bin index 421c9a3..de4b2b7 100644 Binary files a/DMS/dump.bin and b/DMS/dump.bin differ diff --git a/DMS/dump.txt b/DMS/dump.txt index ae181c7..936e482 100644 --- a/DMS/dump.txt +++ b/DMS/dump.txt @@ -2,7 +2,7 @@ Token Dump: Line <1> newline Line <1> newline Line <1> flag -Line <1> name test +Line <1> name main Line <1> newline Line <2> flag Line <2> name warnings @@ -12,9 +12,6 @@ Line <3> flag Line <3> name omniscient Line <3> newline Line <3> newline -Line <4> flag -Line <4> name leaking -Line <4> newline Line <4> newline Line <5> flag Line <5> name debugging @@ -91,34 +88,34 @@ Line <24> string What do you want to do? Line <24> cbracketo { Line <24> newline Line <24> newline -Line <25> string option 0 +Line <25> string option 1 Line <25> name test2 Line <25> parao ( Line <25> string testing Line <25> parac ) Line <25> newline Line <25> newline -Line <26> string option 1 +Line <26> string option 2 Line <26> jump -Line <26> string here +Line <26> string test Line <26> newline Line <26> newline -Line <27> string option 2 +Line <27> string option 3 Line <27> jump Line <27> name there Line <27> newline Line <27> newline -Line <28> string option 3 +Line <28> string option 4 Line <28> gotoo Line <28> string o3 Line <28> newline Line <28> newline -Line <29> string option 4 +Line <29> string option 5 Line <29> gotoo Line <29> name here Line <29> newline Line <29> newline -Line <30> string option 5 +Line <30> string option 6 Line <30> name test Line <30> parao ( Line <30> string here @@ -131,49 +128,64 @@ Line <31> newline Line <32> newline Line <32> newline Line <33> bracketo [ -Line <33> name Bob -Line <33> colon : -Line <33> name char +Line <33> name test Line <33> bracketc ] Line <33> newline Line <33> newline -Line <34> name age -Line <34> equal = -Line <34> number .24 +Line <34> name Ryan +Line <34> colon : +Line <34> string We are here now! Line <34> newline Line <34> newline -Line <35> name money -Line <35> equal = -Line <35> number 100 +Line <35> string hehe Line <35> newline Line <35> newline -Line <36> name excited -Line <36> colon : -Line <36> string path/to/file -Line <36> name function Line <36> newline Line <36> newline +Line <37> bracketo [ +Line <37> name Bob +Line <37> colon : +Line <37> name char +Line <37> bracketc ] Line <37> newline Line <37> newline -Line <38> bracketo [ -Line <38> name newblock -Line <38> colon : -Line <38> name function -Line <38> parao ( -Line <38> parac ) -Line <38> bracketc ] +Line <38> name age +Line <38> equal = +Line <38> number .24 Line <38> newline Line <38> newline -Line <39> string Test #2 +Line <39> name money +Line <39> equal = +Line <39> number 100 Line <39> newline Line <39> newline -Line <40> string Does it parse this part properly? +Line <40> name excited +Line <40> colon : +Line <40> string path/to/file +Line <40> name function Line <40> newline Line <40> newline -Line <41> string huh Line <41> newline Line <41> newline -Line <41> eof +Line <42> bracketo [ +Line <42> name newblock +Line <42> colon : +Line <42> name function +Line <42> parao ( +Line <42> parac ) +Line <42> bracketc ] +Line <42> newline +Line <42> newline +Line <43> string Test #2 +Line <43> newline +Line <43> newline +Line <44> string Does it parse this part properly? +Line <44> newline +Line <44> newline +Line <45> string huh +Line <45> newline +Line <45> newline +Line <45> eof Line <1> newline Line <1> newline Line <1> bracketo [ diff --git a/DMS/dump_bin.txt b/DMS/dump_bin.txt new file mode 100644 index 0000000..421c9a3 Binary files /dev/null and b/DMS/dump_bin.txt differ diff --git a/DMS/errors.h b/DMS/errors.h index 6f0a05a..2184458 100644 --- a/DMS/errors.h +++ b/DMS/errors.h @@ -12,7 +12,8 @@ namespace dms::errors { block_already_defined, choice_unknown, nested_function, - disp_unknown + disp_unknown, + non_existing_block }; struct error { errortype code=unknown; diff --git a/DMS/test.dms b/DMS/test.dms index d76b7ab..efa4ecf 100644 --- a/DMS/test.dms +++ b/DMS/test.dms @@ -1,7 +1,7 @@ -entry test // Will either start the first block seen or the block supplied by you! +entry main // Will either start the first block seen or the block supplied by you! enable warnings disable omniscient -enable leaking +//enable leaking enable debugging loadfile "loadtest.dms" version 1.2 @@ -22,14 +22,18 @@ using extendedDefine Bob: "Testing..." choice "What do you want to do?" { - "option 0" test2("testing") - "option 1" jump "here" - "option 2" jump there - "option 3" goto "o3" - "option 4" goto here - "option 5" test("here") + "option 1" test2("testing") + "option 2" jump "test" + "option 3" jump there + "option 4" goto "o3" + "option 5" goto here + "option 6" test("here") } +[test] + Ryan: "We are here now!" + "hehe" + [Bob:char] age = .24 money = 100 diff --git a/DMS/utils.cpp b/DMS/utils.cpp index cf8f158..34f4e35 100644 --- a/DMS/utils.cpp +++ b/DMS/utils.cpp @@ -36,6 +36,35 @@ namespace dms::utils { std::generate_n(str.begin(), length, randchar); return str; } + bool valueassert(dms_args args, std::unordered_map memory, datatypes t1, datatypes t2, datatypes t3, datatypes t4, datatypes t5, datatypes t6, datatypes t7, datatypes t8, datatypes t9, datatypes t10, datatypes t11, datatypes t12) { + size_t size = args.args.size(); + datatypes types[12] = { t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12 }; + if (size >= 4) + for (size_t i = 0; i < 4; i++) { + if (args.args[i]->resolve(memory)->type != types[i]) + return false; + } + else + for (size_t i = 0; i < size; i++) { + if (args.args[i]->resolve(memory)->type != types[i]) + return false; + } + return true; + } + bool valueassertall(dms_args args, std::unordered_map memory, datatypes t1) { + size_t size = args.args.size(); + if (size >= 4) + for (size_t i = 0; i < 4; i++) { + if (args.args[i]->resolve(memory)->type != t1) + return false; + } + else + for (size_t i = 0; i < size; i++) { + if (args.args[i]->resolve(memory)->type != t1) + return false; + } + return true; + } bool typeassert(dms_args args, datatypes t1, datatypes t2, datatypes t3, datatypes t4, datatypes t5, datatypes t6, datatypes t7, datatypes t8, datatypes t9, datatypes t10, datatypes t11, datatypes t12) { size_t size = args.args.size(); datatypes types[12] = { t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12 }; diff --git a/DMS/utils.h b/DMS/utils.h index 7eb3f3e..c0d249a 100644 --- a/DMS/utils.h +++ b/DMS/utils.h @@ -5,6 +5,7 @@ #include #include #include "value.h" +#include #include "dms_state.h" namespace dms::utils { template @@ -20,6 +21,8 @@ namespace dms::utils { std::string random_string(size_t length); bool typeassert(dms_args args, datatypes t1=nil, datatypes t2 = nil, datatypes t3 = nil, datatypes t4 = nil, datatypes t5 = nil, datatypes t6 = nil, datatypes t7 = nil, datatypes t8 = nil, datatypes t9 = nil, datatypes t10 = nil, datatypes t11 = nil, datatypes t12 = nil); //Type asserting is mostly an internal thing for build in methods. It's not needed for dms code! bool typeassert(dms_state* state, dms_args args, datatypes t1 = nil, datatypes t2 = nil, datatypes t3 = nil, datatypes t4 = nil, datatypes t5 = nil, datatypes t6 = nil, datatypes t7 = nil, datatypes t8 = nil, datatypes t9 = nil, datatypes t10 = nil, datatypes t11 = nil, datatypes t12 = nil); + bool valueassert(dms_args args, std::unordered_map memory, datatypes t1 = nil, datatypes t2 = nil, datatypes t3 = nil, datatypes t4 = nil, datatypes t5 = nil, datatypes t6 = nil, datatypes t7 = nil, datatypes t8 = nil, datatypes t9 = nil, datatypes t10 = nil, datatypes t11 = nil, datatypes t12 = nil); + bool valueassertall(dms_args args, std::unordered_map memory, datatypes t1); std::string resolveTypes(datatypes t1 = nil, datatypes t2 = nil, datatypes t3 = nil, datatypes t4 = nil, datatypes t5 = nil, datatypes t6 = nil, datatypes t7 = nil, datatypes t8 = nil, datatypes t9 = nil, datatypes t10 = nil, datatypes t11 = nil, datatypes t12 = nil); bool isalphanum(std::string str); bool isalpha(std::string str); diff --git a/DMS/value.cpp b/DMS/value.cpp index c91c37d..b5f0a6f 100644 --- a/DMS/value.cpp +++ b/DMS/value.cpp @@ -2,6 +2,7 @@ #include "utils.h" #include "string" namespace dms { + const std::string datatype[] = { "nil", "number", "boolean", "env", "string", "custom", "variable", "block" }; std::vector _VALUES; value::value() { _VALUES.push_back(this); // Used for the interperter! In the end everything is a value diff --git a/DMS/value.h b/DMS/value.h index 9285194..bf8ce07 100644 --- a/DMS/value.h +++ b/DMS/value.h @@ -7,6 +7,9 @@ namespace dms { struct dms_env; + struct value; + struct dms_args; + extern const std::string datatype[]; enum datatypes { nil, number, boolean, env, string, custom, variable, block }; struct dms_number { double val; @@ -35,8 +38,20 @@ namespace dms { return out; }; }; + // Custom data that you can work with by overriding this code struct dms_custom { - + virtual value* Index(value* data); + virtual bool NewIndex(value* var, value* val); + virtual value* Call(dms_args* args); + virtual value* ToString(); + virtual value* ADD(value* left, value* right); + virtual value* SUB(value* left, value* right); + virtual value* MUL(value* left, value* right); + virtual value* DIV(value* left, value* right); + virtual value* POW(value* left, value* right); + virtual value* EQUAL(value* left, value* right); + virtual value* LESS_THAN(value* left, value* right); + virtual value* GREATER_THAN(value* left, value* right); }; dms_string* buildString(std::string str); dms_boolean* buildBool(bool b); diff --git a/README.md b/README.md index 06227b5..8773a54 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ # DMS -The Dialogue Management Script's goal is to provide a nice and simple way to have dialogue for games. +The Dialogue Management Script's goal is to provide a nice and simple way to have dialogue for games. + +# Todo +- Implement bytecode for if/else if/else statements +- Implement bytecode for for/while loops +- Implement bytecode for concatenation +- Interpert all the bytecode +- Finish implementing custom datatype! \ No newline at end of file