diff --git a/DMS/DMS.vcxproj b/DMS/DMS.vcxproj index b188331..81d8d24 100644 --- a/DMS/DMS.vcxproj +++ b/DMS/DMS.vcxproj @@ -147,10 +147,11 @@ - + + - + diff --git a/DMS/DMS.vcxproj.filters b/DMS/DMS.vcxproj.filters index af03e5d..1dcf68d 100644 --- a/DMS/DMS.vcxproj.filters +++ b/DMS/DMS.vcxproj.filters @@ -30,9 +30,6 @@ Source Files\DMS - - Source Files\DMS - Source Files\DMS @@ -54,7 +51,13 @@ Source Files\DMS - + + Source Files\DMS + + + Source Files\DMS + + Source Files\DMS diff --git a/DMS/LineParser.h b/DMS/LineParser.h index 0ed5c01..c8e9788 100644 --- a/DMS/LineParser.h +++ b/DMS/LineParser.h @@ -29,6 +29,10 @@ namespace dms { bool match(tokens::tokentype t1 = tokens::none, tokens::tokentype t2 = tokens::none, tokens::tokentype t3 = tokens::none, tokens::tokentype t4 = tokens::none, tokens::tokentype t5 = tokens::none, tokens::tokentype t6 = tokens::none, tokens::tokentype t7 = tokens::none, tokens::tokentype t8 = tokens::none, tokens::tokentype t9 = tokens::none, tokens::tokentype t10 = tokens::none, tokens::tokentype t11 = tokens::none, tokens::tokentype t12 = tokens::none); bool match(tokens::tokentype* t1 = nullptr, tokens::tokentype* t2 = nullptr, tokens::tokentype* t3 = nullptr, tokens::tokentype* t4 = nullptr, tokens::tokentype* t5 = nullptr, tokens::tokentype* t6 = nullptr, tokens::tokentype* t7 = nullptr, tokens::tokentype* t8 = nullptr, tokens::tokentype* t9 = nullptr, tokens::tokentype* t10 = nullptr, tokens::tokentype* t11 = nullptr, tokens::tokentype* t12 = nullptr); bool hasScope(size_t tabs); + bool restore(size_t p) { + pos = p; + return false; // This is a convience for something I will be doing so much + } }; struct passer { std::string stream; @@ -51,16 +55,28 @@ namespace dms { std::vector temp; size_t tabs = 0; dms_state* state; - + void doCheck(passer* stream, std::vector* t_vec, size_t line, bool& isNum, bool& hasDec, std::vector* buffer); void _Parse(tokenstream stream); // Match Process Code bool match_process_debug(tokenstream* stream); bool match_process_disp(tokenstream* stream); bool match_process_choice(tokenstream* stream); - bool match_process_function(tokenstream* stream); + bool match_process_function(tokenstream* stream, value* v=nullptr); bool match_process_goto(tokenstream* stream); bool match_process_jump(tokenstream* stream); bool match_process_exit(tokenstream* stream); + bool match_process_label(tokenstream* stream); + bool match_process_expression(tokenstream* stream); + // Utils + bool createBlock(std::string bk_name, blocktype bk_type); + + bool isExpression(tokenstream* stream); + bool isBlock(); + bool isBlock(blocktype bk_type); + void tolower(std::string &str); + tokens::tokentype* expr(); + tokens::tokentype* variable(); + void tokenizer(dms_state* state, std::vector &tok); public: //Refer to streams.cpp for the match_process_CMD code. dms_state* Parse(); @@ -68,23 +84,5 @@ namespace dms { dms_state* Parse(dms_state* state, std::string l); LineParser(std::string fn); LineParser(); - //Matches tokens from the stream, if the tokens match it will return true and YOU should call next on the stream. This method does not change the current position - - bool createBlock(std::string bk_name, blocktype bk_type); - bool buildLabel(chunk c, std::string label); - - bool processFunc(tokenstream stream, chunk c); - bool processFunc(tokenstream stream, chunk c, std::string gotoo); - bool processExpr(tokenstream stream, chunk c); - bool processLogic(tokenstream stream, chunk c); - - //Utils - bool isBlock(); - bool isBlock(blocktype bk_type); - - void tolower(std::string &str); - tokens::tokentype* expr(); - tokens::tokentype* variable(); - void tokenizer(dms_state* state, std::vector &tok); }; } \ No newline at end of file diff --git a/DMS/LineParserExtended.cpp b/DMS/LineParserExtended.cpp deleted file mode 100644 index 1fd11d8..0000000 --- a/DMS/LineParserExtended.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "LineParser.h" -using namespace dms::tokens; -using namespace dms::utils; -namespace dms { - bool LineParser::match_process_disp(tokenstream* stream) { - /* - DISP, "msg" - DISP, "msg" speaker - - Compound DISP - */ - if (isBlock(bt_block) && stream->match(tokens::newline, tokens::string, tokens::newline)) { - stream->next(); // Standard consumption - std::string msg = stream->next().name; - print("DISP := ", msg); - cmd* c = new cmd; - c->opcode = codes::DISP; - c->args.push(buildValue(msg)); - current_chunk->addCmd(c); // Add the cmd to the current chunk - return true; - } - else if (isBlock(bt_block) && stream->match(tokens::newline, tokens::name, tokens::colon, tokens::string, tokens::newline)) { - // We might have to handle scope here - // Here we match 'Ryan: "This guy said this!"' Note the colon is needed! - stream->next(); // Standard consumption - std::string name = stream->next().name; - stream->next(); // That colon - std::string msg = stream->next().name; - print("DISP := ", name, " says '", msg, "'"); - cmd* c = new cmd; - c->opcode = codes::DISP; - c->args.push(buildValue(msg)); - c->args.push(buildValue(name)); - current_chunk->addCmd(c); // Add the cmd to the current chunk - // We might have to consume a newline... Depends on what's next - return true; - } - // emotion: "path" - // looks like a simple disp command - else if (isBlock(bt_character) && stream->match(tokens::tab, tokens::name, tokens::colon, tokens::string, tokens::newline)) { - return true; - } - return false; - } - bool LineParser::match_process_debug(tokenstream* stream) { - return false; - } - bool LineParser::match_process_choice(tokenstream* stream) { - return false; - } - bool LineParser::match_process_function(tokenstream* stream) { - return false; - } - bool LineParser::match_process_goto(tokenstream* stream) { - return false; - } - bool LineParser::match_process_jump(tokenstream* stream) { - return false; - } - bool LineParser::match_process_exit(tokenstream* stream) { - return false; - } -} \ No newline at end of file diff --git a/DMS/LineParserMatchProcess.cpp b/DMS/LineParserMatchProcess.cpp new file mode 100644 index 0000000..aa16574 --- /dev/null +++ b/DMS/LineParserMatchProcess.cpp @@ -0,0 +1,183 @@ +#include "LineParser.h" +using namespace dms::tokens; +using namespace dms::utils; +namespace dms { + bool LineParser::match_process_disp(tokenstream* stream) { + /* + DISP, "msg" + DISP, "msg" speaker + + Compound DISP + */ + if (isBlock(bt_block) && stream->match(tokens::newline, tokens::string, tokens::newline)) { + stream->next(); // Standard consumption + std::string msg = stream->next().name; + print("DISP := ", msg); + cmd* c = new cmd; + c->opcode = codes::DISP; + c->args.push(buildValue(msg)); + current_chunk->addCmd(c); // Add the cmd to the current chunk + return true; + } + else if (isBlock(bt_block) && stream->match(tokens::newline, tokens::name, tokens::colon, tokens::string, tokens::newline)) { + // We might have to handle scope here + // Here we match 'Ryan: "This guy said this!"' Note the colon is needed! + stream->next(); // Standard consumption + std::string name = stream->next().name; + stream->next(); // That colon + std::string msg = stream->next().name; + print("DISP := ", name, " says '", msg, "'"); + cmd* c = new cmd; + c->opcode = codes::DISP; + c->args.push(buildValue(msg)); + c->args.push(buildValue(name)); + current_chunk->addCmd(c); // Add the cmd to the current chunk + // We might have to consume a newline... Depends on what's next + return true; + } + // emotion: "path" + // looks like a simple disp command + else if (isBlock(bt_character) && stream->match(tokens::tab, tokens::name, tokens::colon, tokens::string, tokens::newline)) { + return true; + } + + // TODO: We still have to implement the compound disp + + return false; + } + bool LineParser::match_process_debug(tokenstream* stream) { + return false; + } + bool LineParser::match_process_choice(tokenstream* stream) { + return false; + } + /// + /// Recursively parse through function related tokens + /// + /// + /// + /// + bool LineParser::match_process_function(tokenstream* stream, value* v) { + /* + Functions should be able to handle function calls as arguments, + HOWEVER functions cannot be passed as values since they aren't values like they are in other languages! + + The bytecode here is a little tricky, a bit of moving parts and work that the vm has to do. + + If a function has a return that is expected to be consumed, v will not be nullptr and will be pointing to something + The type of v should be variable and if not throw an error! + + The bytecode should look something like this for a given method: + testfunc("Hello!") + off op opcode + 0 FUNC testfunc "Hello" + + The bytecode should look something like this for a given method: + val = testfunc2(2,2) + off op opcode + 0 FUNC testfunc2 val 2 2 + + Notice how we have nil for the first function with no return, this tells the interperter that we do not need to store a value at all + The second method has a varaible to store val so we store that in val + + Given a function: + val = test3(testfunc2(4,4),"Test function as an argument!") + off op opcode + 0 FUNC testfunc2 $A 4 4 + 1 FUNC test3 val $A "Test function as an argument!" + + Not too scary, in fact it's rathar stright forward + Last example: + test4("Testing",test3(1,testfunc2(1,2)),testfunc2(10,100)) + off op opcode + 0 FUNC testfunc2 $A 1 2 + 1 FUNC test3 $B 1 $A + 2 FUNC testfunc2 $C 10 100 + 3 FUNC test4 nil "Testing" $B $C + + A bit more involved, but I'm sure it wasn't too much to follow, especially if you looked at the examples in order + + That's all there is to functions within the bytecode side of things, the vm is where things are a little more involved + */ + if (stream->match(tokens::name, tokens::parao)) { + cmd* c = new cmd; + c->opcode = codes::FUNC; + std::string n = stream->next().name; + print("FUNC ",n); + c->args.push(buildValue(n)); // Set the func identifier as the first variable + // Let's set the target + if (v != nullptr) { + c->args.push(v); // Push the supplied variable + } + else { + c->args.push(buildValue()); // Creates a nil value + } + // Already we have built: FUNC name val + // Next we add arguments this is where things get interesting + tokenstream tempstream; + // This is a balanced consuming method (()(())) + std::vector t = stream->next(tokens::parao, tokens::parac); + tempstream.init(&t); + tokens::token tok; + while (tempstream.peek().type != tokens::none) { // End of stream + if (tempstream.match(tokens::string)) { + // We have a string argument + } + else if (tempstream.match(tokens::number)) { + // This is an interesting one This could be just a number, or an expression with functions n stuff + } + else if (match_process_expression(&tempstream)) { + // We have an expression and its been handled Whoo!!! + } + // Final match this could be a function it might also be an expression + else if (match_process_function(&tempstream, buildVariable(""))) { + + } + else if (tempstream.match(tokens::seperator)) { + // We have a seperator for function arguments + tempstream.next(); // Consume it + } + } + /*std::cout << "---Tokens---" << std::endl; + for (size_t i = 0; i < t.size() - 1; i++) { + std::cout << t[i] << std::endl; + }*/ + current_chunk->addCmd(c); // We push this onto the chunk after all dependants if any have been handled + wait(); + } + return false; + } + bool LineParser::match_process_goto(tokenstream* stream) { + return false; + } + bool LineParser::match_process_jump(tokenstream* stream) { + return false; + } + bool LineParser::match_process_exit(tokenstream* stream) { + return false; + } + bool LineParser::match_process_label(tokenstream* stream) { + return false; + } + bool LineParser::match_process_expression(tokenstream* stream) { + // I will have to consume for this to work so we need to keep track of what was incase we return false! + size_t start_pos = stream->pos; + // It has to start with one of these 3 to even be considered an expression + if (stream->match(tokens::number) || stream->match(tokens::name) || stream->match(tokens::parao)) { + // What do we know, math expressions can only be on a single line. We know where to stop looking if we have to + token t = stream->peek(); + if (t.type == tokens::parao) { + tokenstream temp; + temp.init(&(stream->next(tokens::parao, tokens::parac))); // Balanced match! + return match_process_expression(&temp); // Return true is everything else checks out! + } + else if (t.type == tokens::number) { + + } + } + else { + return stream->restore(start_pos); // Always return false and restores the position in stream! + } + } + } +} \ No newline at end of file diff --git a/DMS/LineParser.cpp b/DMS/LineParserParse.cpp similarity index 69% rename from DMS/LineParser.cpp rename to DMS/LineParserParse.cpp index 2f5dc62..0c705f6 100644 --- a/DMS/LineParser.cpp +++ b/DMS/LineParserParse.cpp @@ -3,430 +3,6 @@ using namespace dms::tokens; using namespace dms::utils; namespace dms { - void tokenstream::init(std::vector* ptr) { - this->tokens = *ptr; - } - token tokenstream::next() { - if (pos > this->tokens.size()) - return token{ tokentype::noop,codes::NOOP,"EOF",0 }; - return this->tokens[pos++]; - } - void tokenstream::prev() { - pos--; - } - std::vector tokenstream::next(tokentype to, tokentype tc) { - std::vector tok; - size_t open = 0; - if (peek().type == to) { - open++; - next(); // Consume - while (open != 0) { - if (peek().type == to) - open++; - else if (peek().type == tc) - open--; - tok.push_back(next()); - } - } - return tok; - } - token tokenstream::peek() { - return this->tokens[pos]; - } - bool tokenstream::hasScope(size_t tabs) { - return false; - } - std::vector tokenstream::next(tokentype tk) { - std::vector temp; - while (peek().type!=tk) { - temp.push_back(next()); - } - temp.push_back(next()); - return temp; - } - uint8_t passer::next() { - if (stream.size() == pos) { - return NULL; - } - else { - return stream[pos++]; - } - } - void passer::next(uint8_t c) { - next(); - while (peek() != c) { - next(); - } - } - uint8_t passer::prev() { - if (0 == pos) { - return NULL; - } - return stream[--pos]; - } - uint8_t passer::peek() { - if (stream.size() == pos) { - return NULL; - } - return stream[pos]; - } - std::string passer::processBuffer(std::vector buf) { - return std::string(buf.begin(), buf.end()); - } - bool LineParser::isBlock() { - return isBlock(bt_block); // Default block type - } - bool LineParser::isBlock(blocktype bk_type) { - if (current_chunk == nullptr) { - return false; // If a chunk wasn't defined then code was probably defined outside of a block - } - return current_chunk->type == bk_type; - } - void doCheck(passer* stream,std::vector* t_vec, size_t line, bool &isNum, bool &hasDec, std::vector* buffer) { - std::string str = stream->processBuffer(*buffer); - if (utils::isNum(str) && isNum) { - t_vec->push_back(token{ tokens::number,codes::NOOP,stream->processBuffer(*buffer),line }); - buffer->clear(); - isNum = false; - hasDec = false; - } - else if (buffer->size() > 0) { - if (str == "nil") { - t_vec->push_back(token{ tokens::nil,codes::NOOP,stream->processBuffer(*buffer),line }); - buffer->clear(); - hasDec = false; - } - else if (str == "true") { - t_vec->push_back(token{ tokens::True,codes::NOOP,stream->processBuffer(*buffer),line }); - buffer->clear(); - hasDec = false; - } - else if (str == "false") { - t_vec->push_back(token{ tokens::False,codes::NOOP,stream->processBuffer(*buffer),line }); - buffer->clear(); - hasDec = false; - } - else if (utils::isNum(str) && str.size() > 0) { - t_vec->push_back(token{ tokens::number,codes::NOOP,stream->processBuffer(*buffer),line }); - buffer->clear(); - isNum = false; - hasDec = false; - } - else if (utils::isalphanum(str) && str.size() > 0) { - t_vec->push_back(token{ tokens::name,codes::NOOP,stream->processBuffer(*buffer),line }); - buffer->clear(); - hasDec = false; - } - else { - t_vec->push_back(token{ tokens::name,codes::ERRO,"Invalid variable name!",line }); - } - } - } - void wait() { - std::cin.ignore(); - } - bool tokenstream::match(tokens::tokentype t1, tokens::tokentype t2, tokens::tokentype t3, tokens::tokentype t4, tokens::tokentype t5, tokens::tokentype t6, tokens::tokentype t7, tokens::tokentype t8, tokens::tokentype t9, tokens::tokentype t10, tokens::tokentype t11, tokens::tokentype t12) { - tokens::tokentype types[12] = { t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12 }; - for (size_t i = 0; i < 12; i++) { - if (types[i] == tokens::none) - return true; - if (this->tokens[pos+i].type != types[i]) - return false; - } - return true; - } - tokentype* LineParser::expr() { - return new tokentype[9]{ tokens::name,tokens::number,tokens::divide,tokens::minus,tokens::mod,tokens::multiply,tokens::plus,tokens::pow ,tokens::none }; - // tokens::none tells us we are at the end of the array. - } - tokentype* LineParser::variable() { - return new tokentype[7]{tokens::name,tokens::number,tokens::True,tokens::False,tokens::nil,tokens::string,tokens::none}; - // tokens::none tells us we are at the end of the array. - } - bool inList(tokens::tokentype t,tokens::tokentype* list) { - size_t c = 0; - while (list[c] != tokens::none) { - if (list[c] == t) - return true; - } - return false; - } - bool tokenstream::match(tokens::tokentype* t1, tokens::tokentype* t2, tokens::tokentype* t3, tokens::tokentype* t4, tokens::tokentype* t5, tokens::tokentype* t6, tokens::tokentype* t7, tokens::tokentype* t8, tokens::tokentype* t9, tokens::tokentype* t10, tokens::tokentype* t11, tokens::tokentype* t12) { - tokens::tokentype* types[12] = { t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12 }; - for (size_t i = 0; i < 12; i++) { - if (types[i] == nullptr) - return true; - if (!inList(this->tokens[pos + i].type, types[i])) - return false; - } - return true; - } - - // Handling functions are important. This method is a helper function that allows you to - bool LineParser::processFunc(tokenstream stream, chunk c) { - // - return false; - } - bool processFunc(tokenstream stream, chunk c, std::string gotoo) { - return true; - } - bool LineParser::processExpr(tokenstream stream, chunk c) { - // - return false; - } - bool LineParser::processLogic(tokenstream stream, chunk c) { - // - return false; - } - bool LineParser::createBlock(std::string bk_name, blocktype bk_type) { - if (current_chunk != nullptr) { - if (!state->chunks.count(current_chunk->name)) - state->chunks.insert_or_assign(current_chunk->name, current_chunk); - else - { - std::stringstream str; - str << "Block <" << current_chunk->name << "> already defined!"; - state->push_error(errors::error{ errors::block_already_defined,str.str(),true,line }); - return false; - } - } - current_chunk = new chunk; - current_chunk->name = bk_name; - chunk_type = bk_type; - current_chunk->type = bk_type; - print("Created Block: ",bk_name," <",bk_type,">"); - return true; - } - void LineParser::_Parse(tokenstream stream) { - token current = stream.next(); - while (stream.peek().type != tokens::eof) { - print(current); - if (current.type == tokens::flag) { - temp = stream.next(tokens::newline); - stream.prev(); // Unconsume the newline piece - if (temp.size() != 2) { - std::cout << "Error"; - } - codes::op code = current.raw; - tokens::tokentype tok = temp[0].type; - if (code == codes::ENAB && tok == tokens::name) { - tolower(temp[0].name); - state->enables.insert_or_assign(temp[0].name, true); - } - else if (code == codes::ENTR && tok == tokens::name) { - state->entry = temp[0].name; - } - else if (code == codes::DISA && tok == tokens::name) { - tolower(temp[0].name); - state->enables.insert_or_assign(temp[0].name, false); - } - else if (code == codes::VERN && tok == tokens::number) { - state->version = std::stod(temp[0].name); - } - else if (code == codes::USIN && tok == tokens::name) { - // TODO add usings, kinda useless atm since everything will be packed in. Perhaps extensions? - } - else if (code == codes::LOAD && tok == tokens::string) { - print("Loading File: ",temp[0].name); - LineParser parser = LineParser(); - parser.Parse(state, temp[0].name);// Load another file - } - else { - std::stringstream str; - str << "Expected " << " got: " << current << temp[0]; - state->push_error(errors::error{ errors::badtoken,str.str(),true,line }); - } - } - // Default block - if (stream.match(tokens::newline, tokens::bracketo, tokens::name, tokens::bracketc)) { - stream.next(); - std::string name = stream.next().name; - createBlock(name,bt_block); - line = stream.next().line_num; // Consume - } - // This handles a few block types since they all follow a similar format - else if (stream.match(tokens::newline, tokens::bracketo, tokens::name, tokens::colon, tokens::name, tokens::bracketc)) { - stream.next(); - stream.next(); - std::string name = stream.next().name; - line = stream.next().line_num; - std::string temp = stream.next().name; - // Characters are a feature I want to have intergrated into the language - if (temp == "char") { - createBlock(name, bt_character); - } - // Enviroments are sortof like objects, they can be uses as an object. They are a cleaner way to build a hash map like object - else if (temp == "env") { - createBlock(name, bt_env); - } - // Menus are what they say on the tin. They provide the framework for having menus within your game - else if (temp == "menu") { - createBlock(name, bt_menu); - } - } - // Function block type - else if (stream.match(tokens::newline, tokens::bracketo, tokens::name, tokens::colon, tokens::name, tokens::parao)) { - std::stringstream str; - stream.next(); - stream.next(); - std::string name = stream.next().name; - line = stream.next().line_num; // The color, not needed after the inital match, but we still need to consume it - std::string b = stream.next().name; - if (b == "function") { - createBlock(name, bt_method); // We have a method let's set the block type to that, but we aren't done yet - // We need to set the params if any so the method can be supplied with arguments - stream.next(); // parao - std::vector tokens = stream.next(tokens::parac); // Consume until we see parac - dms_args args; - for (size_t i = 0; i < tokens.size() - 1; i++) {//The lase symbol is parac since that was the consume condition - if (tokens[i].type == tokens::name) { - // We got a name which is refering to a variable so lets build one - value* v = new value{}; - v->type = datatypes::variable; // Special type, it writes data to the string portion, but is interperted as a lookup - v->s = buildString(tokens[i].name); - args.push(v); - } - else if (tokens[i].type == tokens::seperator) { - // We just ignore this - } - else { - std::stringstream str; - str << "Unexpected symbol: " << tokens[i]; - state->push_error(errors::error{ errors::badtoken,str.str(),true,line }); - } - } - // If all went well the 'args' now has all of tha params for the method we will be working with - current_chunk->params = args; - // Thats should be all we need to do - } - else { - str << "'function' keyword expected got " << b; - state->push_error(errors::error{ errors::badtoken, str.str(),true,line }); - } - } - // Control Handle all controls here - if (stream.match(tokens::control)) { - token control = stream.next(); - if (control.raw == codes::CHOI && stream.peek().type == tokens::string) { - // Let's parse choice blocks. - std::string prompt = stream.next().name; - print("Prompt: ", prompt); - bool good = true; - std::string option; - cmd* c = new cmd; - // Create a unique label name by using the line number - std::string choicelabel = concat("$CHOI_END_", stream.peek().line_num); - wait(); - c->opcode = codes::CHOI; - c->args.push(buildValue(prompt)); - current_chunk->addCmd(c); // We will keep a reference to this and add to it as we go through the list - - /* - What's going on here might be tough to understand just by looking at the code - The bytecode generated by this code might look something like this: - - off op opcodes - 0 CHOI "Pick one!" "Choice 1" "Choice 2" "Choice 3" "Choice 4" - 1 FUNC print "You picked 1!" - 2 GOTO $CHOI_END_1 - 3 FUNC print "You picked 2!" - 4 GOTO $CHOI_END_1 - 5 JUMP park - 6 NOOP - 7 GOTO mylabel - 8 LABL $CHOI_END_1 - - The CHOI code tells the vm that we need to process user input. The input we get in a number 0-3 - I know we have 4 choices - If the user provides us with a 0 then we need to move to off 1 - If the user provides us with a 1 then we need to move to off 3 - If the user provides us with a 2 then we need to move to off 5 - If the user provides us with a 3 then we need to move to off 7 - I'm sure you see the pattern here. 1 (+2) 3 (+2) 5... We only need to jump once then let the vm continue like normal. - The math for this is: [current_pos] + (n*2+1) - n*2+1 (n = 0) = 1 - n*2+1 (n = 1) = 3 - n*2+1 (n = 2) = 5 - n*2+1 (n = 3) = 7 - Which is why you see NOOP for the JUMP code. If GOTO wasn't the last choice possible to make there would be a NOOP after that as well. - The NOOP ensures the pattern stays. - If we are provided with a number greater than 3 then we can push an execption. - */ - while (!stream.match(tokens::cbracketc)) { - // We need to match the possible options for a choice block - /* - "option" function() - "option" goto "" - "option" goto var - "option" jump "" - "option" jump var - "option" exit [0] - - Exit takes an optional int - */ - if (stream.match(tokens::string)) { - std::string name = stream.next().name; - c->args.push(buildValue(name)); // We append the choice to the first part of the CHOI cmd - - // We consumed the option now lets do some matching, note that all of these are one liners in the bytecode! - if (match_process_function(&stream)) { - - } - else if (match_process_goto(&stream)) { - - } - else if (match_process_jump(&stream)) { - - } - else if (match_process_exit(&stream)) { - - } - } - // Last Match - else if (stream.match(tokens::newline)) { - stream.next(); // Consume - } - else if (!stream.match(tokens::cbracketc)) { - state->push_error(errors::error{errors::choice_unknown,"Unexpected symbol!"}); - } - } - } - else if (control.raw == codes::IFFF) { - // This will probably be the toughest one of them all - } - } - // Displays both with a target and without - match_process_disp(&stream); - - // function stuff - /*if (match(stream, tokens::name, tokens::parao)) { - std::string n = stream.next().name; - std::vector t = stream.next(tokens::parao, tokens::parac); - std::cout << "---Tokens---" << std::endl; - for (size_t i = 0; i < t.size()-1; i++) { - std::cout << t[i] << std::endl; - } - wait(); - }*/ - if (current.type != tokens::tab) - tabs = 0; - current = stream.next(); - } - state->chunks.insert_or_assign(current_chunk->name, current_chunk); - } - void LineParser::tokenizer(dms_state* state,std::vector &toks) { - tokenstream stream; - stream.init(&toks); - this->state = state; // Grab the pointer to the state and store it within the parser object - _Parse(stream); - } - void LineParser::tolower(std::string &s1) { - std::transform(s1.begin(), s1.end(), s1.begin(), std::tolower); - } - LineParser::LineParser(std::string f) { - fn = f; - } - LineParser::LineParser() {} dms_state* dms::LineParser::Parse() { if (fn == "") { std::cout << "ERROR: You did not provide the constructor with a file path!" << std::endl; @@ -439,7 +15,7 @@ namespace dms { return Parse(state, fn); } dms_state* dms::LineParser::Parse(dms_state* state, std::string file) { - std::map chunks; + std::unordered_map chunks; std::vector t_vec; std::string li; std::ifstream myfile(file); @@ -471,11 +47,11 @@ namespace dms { bool hasDec = false; bool labelStart = false; size_t line = 1; - while (data != NULL) { - if (data == '/' && stream.peek()=='/') { + while (data != NULL) { + if (data == '/' && stream.peek() == '/') { //line++; stream.next('\n'); // Seek until you find a newline - } + } else if (data == '\n') { doCheck(&stream, &t_vec, line, isNum, hasDec, &buffer); t_vec.push_back(token{ tokens::newline,codes::NOOP,"",line }); @@ -489,8 +65,8 @@ namespace dms { } else if (data == '"' && !isStr) { isStr = true; - } - else if (data == ':' && stream.peek()==':' && !labelStart) { + } + else if (data == ':' && stream.peek() == ':' && !labelStart) { labelStart = true; stream.next(); } @@ -509,7 +85,7 @@ namespace dms { t_vec.push_back(token{ tokens::string,codes::NOOP,stream.processBuffer(buffer),line }); buffer.clear(); } - else if (isdigit(data) ) { + else if (isdigit(data)) { isNum = true; buffer.push_back(data); } @@ -605,7 +181,8 @@ namespace dms { tolower(str); if (str == "enable") { t_vec.push_back(token{ tokens::flag,codes::ENAB,"",line }); - } else if (str == "entry") { + } + else if (str == "entry") { t_vec.push_back(token{ tokens::flag,codes::ENTR,"",line }); } else if (str == "loadfile") { @@ -662,7 +239,10 @@ namespace dms { else if (str == "jump") { t_vec.push_back(token{ tokens::jump,codes::NOOP,"",line }); } - else if (utils::isalphanum(str) && str.size()>0) { + else if (str == "exit") { + t_vec.push_back(token{ tokens::exit,codes::NOOP,"",line }); + } + else if (utils::isalphanum(str) && str.size() > 0) { t_vec.push_back(token{ tokens::name,codes::NOOP,stream.processBuffer(buffer),line }); } else { @@ -674,8 +254,8 @@ namespace dms { buffer.clear(); } data = stream.next(); - } - t_vec.push_back(token{ tokens::eof,codes::NOOP,"",line+1 }); + } + t_vec.push_back(token{ tokens::eof,codes::NOOP,"",line + 1 }); std::ofstream outputFile("dump.txt"); outputFile << "Token Dump:" << std::endl; for (size_t i = 0; i < t_vec.size(); i++) { @@ -687,4 +267,219 @@ namespace dms { tokenizer(state, t_vec); return state; } + void LineParser::_Parse(tokenstream stream) { + token current = stream.next(); + while (stream.peek().type != tokens::eof) { + print(current); + if (current.type == tokens::flag) { + temp = stream.next(tokens::newline); + stream.prev(); // Unconsume the newline piece + if (temp.size() != 2) { + std::cout << "Error"; + } + codes::op code = current.raw; + tokens::tokentype tok = temp[0].type; + if (code == codes::ENAB && tok == tokens::name) { + tolower(temp[0].name); + state->enables.insert_or_assign(temp[0].name, true); + } + else if (code == codes::ENTR && tok == tokens::name) { + state->entry = temp[0].name; + } + else if (code == codes::DISA && tok == tokens::name) { + tolower(temp[0].name); + state->enables.insert_or_assign(temp[0].name, false); + } + else if (code == codes::VERN && tok == tokens::number) { + state->version = std::stod(temp[0].name); + } + else if (code == codes::USIN && tok == tokens::name) { + // TODO add usings, kinda useless atm since everything will be packed in. Perhaps extensions? + } + else if (code == codes::LOAD && tok == tokens::string) { + print("Loading File: ", temp[0].name); + LineParser parser = LineParser(); + parser.Parse(state, temp[0].name);// Load another file + } + else { + std::stringstream str; + str << "Expected " << " got: " << current << temp[0]; + state->push_error(errors::error{ errors::badtoken,str.str(),true,line }); + } + } + // Default block + if (stream.match(tokens::newline, tokens::bracketo, tokens::name, tokens::bracketc)) { + stream.next(); + std::string name = stream.next().name; + createBlock(name, bt_block); + line = stream.next().line_num; // Consume + } + // This handles a few block types since they all follow a similar format + else if (stream.match(tokens::newline, tokens::bracketo, tokens::name, tokens::colon, tokens::name, tokens::bracketc)) { + stream.next(); + stream.next(); + std::string name = stream.next().name; + line = stream.next().line_num; + std::string temp = stream.next().name; + // Characters are a feature I want to have intergrated into the language + if (temp == "char") { + createBlock(name, bt_character); + } + // Enviroments are sortof like objects, they can be uses as an object. They are a cleaner way to build a hash map like object + else if (temp == "env") { + createBlock(name, bt_env); + } + // Menus are what they say on the tin. They provide the framework for having menus within your game + else if (temp == "menu") { + createBlock(name, bt_menu); + } + } + // Function block type + else if (stream.match(tokens::newline, tokens::bracketo, tokens::name, tokens::colon, tokens::name, tokens::parao)) { + std::stringstream str; + stream.next(); + stream.next(); + std::string name = stream.next().name; + line = stream.next().line_num; // The color, not needed after the inital match, but we still need to consume it + std::string b = stream.next().name; + if (b == "function") { + createBlock(name, bt_method); // We have a method let's set the block type to that, but we aren't done yet + // We need to set the params if any so the method can be supplied with arguments + stream.next(); // parao + std::vector tokens = stream.next(tokens::parac); // Consume until we see parac + dms_args args; + for (size_t i = 0; i < tokens.size() - 1; i++) {//The lase symbol is parac since that was the consume condition + if (tokens[i].type == tokens::name) { + // We got a name which is refering to a variable so lets build one + value* v = new value{}; + v->type = datatypes::variable; // Special type, it writes data to the string portion, but is interperted as a lookup + v->s = buildString(tokens[i].name); + args.push(v); + } + else if (tokens[i].type == tokens::seperator) { + // We just ignore this + } + else { + std::stringstream str; + str << "Unexpected symbol: " << tokens[i]; + state->push_error(errors::error{ errors::badtoken,str.str(),true,line }); + } + } + // If all went well the 'args' now has all of tha params for the method we will be working with + current_chunk->params = args; + // Thats should be all we need to do + } + else { + str << "'function' keyword expected got " << b; + state->push_error(errors::error{ errors::badtoken, str.str(),true,line }); + } + } + // Control Handle all controls here + if (stream.match(tokens::control)) { + token control = stream.next(); + if (control.raw == codes::CHOI && stream.peek().type == tokens::string) { + // Let's parse choice blocks. + std::string prompt = stream.next().name; + print("Prompt: ", prompt); + bool good = true; + std::string option; + cmd* c = new cmd; + // Create a unique label name by using the line number + std::string choicelabel = concat("$CHOI_END_", stream.peek().line_num); + wait(); + c->opcode = codes::CHOI; + c->args.push(buildValue(prompt)); + current_chunk->addCmd(c); // We will keep a reference to this and add to it as we go through the list + bool start = false; + /* + What's going on here might be tough to understand just by looking at the code + The bytecode generated by this code might look something like this: + + off op opcodes + 0 CHOI "Pick one!" "Choice 1" "Choice 2" "Choice 3" "Choice 4" + 1 FUNC print "You picked 1!" + 2 GOTO $CHOI_END_1 + 3 FUNC print "You picked 2!" + 4 GOTO $CHOI_END_1 + 5 JUMP park + 6 NOOP + 7 GOTO mylabel + 8 LABL $CHOI_END_1 + + The CHOI code tells the vm that we need to process user input. The input we get in a number 0-3 + I know we have 4 choices + If the user provides us with a 0 then we need to move to off 1 + If the user provides us with a 1 then we need to move to off 3 + If the user provides us with a 2 then we need to move to off 5 + If the user provides us with a 3 then we need to move to off 7 + I'm sure you see the pattern here. 1 (+2) 3 (+2) 5... We only need to jump once then let the vm continue like normal. + The math for this is: [current_pos] + (n*2+1) + n*2+1 (n = 0) = 1 + n*2+1 (n = 1) = 3 + n*2+1 (n = 2) = 5 + n*2+1 (n = 3) = 7 + Which is why you see NOOP for the JUMP code. If GOTO wasn't the last choice possible to make there would be a NOOP after that as well. + The NOOP ensures the pattern stays. + If we are provided with a number greater than 3 then we can push an execption. + */ + while (!stream.match(tokens::cbracketc)) { + // We need to match the possible options for a choice block + /* + "option" function() + "option" goto "" + "option" goto var + "option" jump "" + "option" jump var + "option" exit [0] + + Exit takes an optional int + */ + if (stream.match(tokens::cbracketo) && !start) { + start = true; + stream.next(); + } + else if (stream.match(tokens::cbracketo) && start) { + state->push_error(errors::error{ errors::choice_unknown,concat("Unexpected symbol ",stream.next()),true,stream.peek().line_num }); + } + else if (stream.match(tokens::string)) { + std::string name = stream.next().name; + print("Option: ", name); + c->args.push(buildValue(name)); // We append the choice to the first part of the CHOI cmd + + // We consumed the option now lets do some matching, note that all of these are one liners in the bytecode! + if (match_process_function(&stream)) { + + } + else if (match_process_goto(&stream)) { + + } + else if (match_process_jump(&stream)) { + + } + else if (match_process_exit(&stream)) { + + } + } + // Last Match + else if (stream.match(tokens::newline)) { + stream.next(); // Consume + } + else if (!stream.match(tokens::cbracketc)) { + state->push_error(errors::error{ errors::choice_unknown,concat("Unexpected symbol ",stream.next()),true,stream.peek().line_num }); + } + } + } + else if (control.raw == codes::IFFF) { + // This will probably be the toughest one of them all + } + } + // Displays both with a target and without + match_process_disp(&stream); + + if (current.type != tokens::tab) + tabs = 0; + current = stream.next(); + } + state->chunks.insert_or_assign(current_chunk->name, current_chunk); + } } \ No newline at end of file diff --git a/DMS/LineParserUtils.cpp b/DMS/LineParserUtils.cpp new file mode 100644 index 0000000..7624c8d --- /dev/null +++ b/DMS/LineParserUtils.cpp @@ -0,0 +1,195 @@ +#include "LineParser.h" +#include "errors.h" +using namespace dms::tokens; +using namespace dms::utils; +namespace dms { + void tokenstream::init(std::vector* ptr) { + this->tokens = *ptr; + } + token tokenstream::next() { + if (pos > this->tokens.size()) + return token{ tokentype::none,codes::NOOP,"EOS",0 }; + return this->tokens[pos++]; + } + void tokenstream::prev() { + pos--; + } + std::vector tokenstream::next(tokentype to, tokentype tc) { + std::vector tok; + size_t open = 0; + if (peek().type == to) { + open++; + next(); // Consume + while (open != 0) { + if (peek().type == to) + open++; + else if (peek().type == tc) + open--; + tok.push_back(next()); + } + } + return tok; + } + token tokenstream::peek() { + if (pos > this->tokens.size()) + return token{ tokentype::none,codes::NOOP,"EOS",0 }; + return this->tokens[pos]; + } + bool tokenstream::hasScope(size_t tabs) { + return false; + } + std::vector tokenstream::next(tokentype tk) { + std::vector temp; + while (peek().type!=tk) { + temp.push_back(next()); + } + temp.push_back(next()); + return temp; + } + uint8_t passer::next() { + if (stream.size() == pos) { + return NULL; + } + else { + return stream[pos++]; + } + } + void passer::next(uint8_t c) { + next(); + while (peek() != c) { + next(); + } + } + uint8_t passer::prev() { + if (0 == pos) { + return NULL; + } + return stream[--pos]; + } + uint8_t passer::peek() { + if (stream.size() == pos) { + return NULL; + } + return stream[pos]; + } + std::string passer::processBuffer(std::vector buf) { + return std::string(buf.begin(), buf.end()); + } + bool LineParser::isBlock() { + return isBlock(bt_block); // Default block type + } + bool LineParser::isBlock(blocktype bk_type) { + if (current_chunk == nullptr) { + return false; // If a chunk wasn't defined then code was probably defined outside of a block + } + return current_chunk->type == bk_type; + } + bool tokenstream::match(tokens::tokentype t1, tokens::tokentype t2, tokens::tokentype t3, tokens::tokentype t4, tokens::tokentype t5, tokens::tokentype t6, tokens::tokentype t7, tokens::tokentype t8, tokens::tokentype t9, tokens::tokentype t10, tokens::tokentype t11, tokens::tokentype t12) { + tokens::tokentype types[12] = { t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12 }; + for (size_t i = 0; i < 12; i++) { + if (types[i] == tokens::none) + return true; + if (this->tokens[pos+i].type != types[i]) + return false; + } + return true; + } + tokentype* LineParser::expr() { + return new tokentype[9]{ tokens::name,tokens::number,tokens::divide,tokens::minus,tokens::mod,tokens::multiply,tokens::plus,tokens::pow ,tokens::none }; + // tokens::none tells us we are at the end of the array. + } + tokentype* LineParser::variable() { + return new tokentype[7]{tokens::name,tokens::number,tokens::True,tokens::False,tokens::nil,tokens::string,tokens::none}; + // tokens::none tells us we are at the end of the array. + } + bool inList(tokens::tokentype t,tokens::tokentype* list) { + size_t c = 0; + while (list[c] != tokens::none) { + if (list[c] == t) + return true; + } + return false; + } + bool tokenstream::match(tokens::tokentype* t1, tokens::tokentype* t2, tokens::tokentype* t3, tokens::tokentype* t4, tokens::tokentype* t5, tokens::tokentype* t6, tokens::tokentype* t7, tokens::tokentype* t8, tokens::tokentype* t9, tokens::tokentype* t10, tokens::tokentype* t11, tokens::tokentype* t12) { + tokens::tokentype* types[12] = { t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12 }; + for (size_t i = 0; i < 12; i++) { + if (types[i] == nullptr) + return true; + if (!inList(this->tokens[pos + i].type, types[i])) + return false; + } + return true; + } + void LineParser::doCheck(passer* stream, std::vector* t_vec, size_t line, bool& isNum, bool& hasDec, std::vector* buffer) { + std::string str = stream->processBuffer(*buffer); + if (utils::isNum(str) && isNum) { + t_vec->push_back(token{ tokens::number,codes::NOOP,stream->processBuffer(*buffer),line }); + buffer->clear(); + isNum = false; + hasDec = false; + } + else if (buffer->size() > 0) { + if (str == "nil") { + t_vec->push_back(token{ tokens::nil,codes::NOOP,stream->processBuffer(*buffer),line }); + buffer->clear(); + hasDec = false; + } + else if (str == "true") { + t_vec->push_back(token{ tokens::True,codes::NOOP,stream->processBuffer(*buffer),line }); + buffer->clear(); + hasDec = false; + } + else if (str == "false") { + t_vec->push_back(token{ tokens::False,codes::NOOP,stream->processBuffer(*buffer),line }); + buffer->clear(); + hasDec = false; + } + else if (utils::isNum(str) && str.size() > 0) { + t_vec->push_back(token{ tokens::number,codes::NOOP,stream->processBuffer(*buffer),line }); + buffer->clear(); + isNum = false; + hasDec = false; + } + else if (utils::isalphanum(str) && str.size() > 0) { + t_vec->push_back(token{ tokens::name,codes::NOOP,stream->processBuffer(*buffer),line }); + buffer->clear(); + hasDec = false; + } + else { + t_vec->push_back(token{ tokens::name,codes::ERRO,"Invalid variable name!",line }); + } + } + } + bool LineParser::createBlock(std::string bk_name, blocktype bk_type) { + if (current_chunk != nullptr) { + if (!state->chunks.count(current_chunk->name)) + state->chunks.insert_or_assign(current_chunk->name, current_chunk); + else + { + std::stringstream str; + str << "Block <" << current_chunk->name << "> already defined!"; + state->push_error(errors::error{ errors::block_already_defined,str.str(),true,line }); + return false; + } + } + current_chunk = new chunk; + current_chunk->name = bk_name; + chunk_type = bk_type; + current_chunk->type = bk_type; + print("Created Block: ",bk_name," <",bk_type,">"); + return true; + } + void LineParser::tokenizer(dms_state* state,std::vector &toks) { + tokenstream stream; + stream.init(&toks); + this->state = state; // Grab the pointer to the state and store it within the parser object + _Parse(stream); + } + void LineParser::tolower(std::string &s1) { + std::transform(s1.begin(), s1.end(), s1.begin(), std::tolower); + } + LineParser::LineParser(std::string f) { + fn = f; + } + LineParser::LineParser() {} +} \ No newline at end of file diff --git a/DMS/codes.cpp b/DMS/codes.cpp index 40e25e1..2deedc1 100644 --- a/DMS/codes.cpp +++ b/DMS/codes.cpp @@ -1,3 +1,3 @@ #pragma once #include "Codes.h" -const std::string dms::codes::list[] = { "NOOP","ENTR","ENAB","DISA","LOAD","VERN","USIN","STAT","DISP","ASGN","LABL","CHOI","OPTN","FORE","????","WHLE","FNWR","FNNR","IFFF","ELIF","ELSE","DEFN","SKIP","COMP","INDX","JMPZ","INST","ERRO" ,"RETN" }; \ No newline at end of file +const std::string dms::codes::list[] = { "NOOP","ENTR","ENAB","DISA","LOAD","VERN","USIN","STAT","DISP","ASGN","LABL","CHOI","OPTN","FORE","????","WHLE","FUNC","IFFF","ELIF","ELSE","DEFN","SKIP","COMP","INDX","JMPZ","INST","ERRO" ,"RETN" }; \ No newline at end of file diff --git a/DMS/codes.h b/DMS/codes.h index 26b340b..52dcf22 100644 --- a/DMS/codes.h +++ b/DMS/codes.h @@ -19,8 +19,7 @@ namespace dms::codes { FORE, UNWN, WHLE, - FNWR, - FNNR, + FUNC, IFFF, ELIF, ELSE, diff --git a/DMS/dms_state.h b/DMS/dms_state.h index 515c6af..8020619 100644 --- a/DMS/dms_state.h +++ b/DMS/dms_state.h @@ -3,6 +3,7 @@ #include "chunk.h" #include #include +#include namespace dms { class dms_state { @@ -10,8 +11,8 @@ namespace dms { void push_error(errors::error err); void push_warning(errors::error err); double version=1.0; - std::map chunks; + std::unordered_map chunks; std::string entry = "start"; - std::map enables; + std::unordered_map enables; }; } diff --git a/DMS/test.dms b/DMS/test.dms index c541bc4..917b585 100644 --- a/DMS/test.dms +++ b/DMS/test.dms @@ -50,10 +50,10 @@ using extendedDefine //Hello im testing stuff CHOICE "Pick one:" { - "first" func() - "second" func() - "third" func() - "forth" func() + "first" func(1,2,3) + "second" func(true,false) + "third" func("hehe") + "forth" func("1",2,false) "fifth" goto "here" "sixth" goto name "sevinth" jump "there" diff --git a/DMS/token.h b/DMS/token.h index ef6e6d9..42a168e 100644 --- a/DMS/token.h +++ b/DMS/token.h @@ -39,6 +39,7 @@ namespace dms::tokens { ret, gotoo, jump, + exit, nil };//stream, t_vec, line, isNum, buffer struct token { @@ -97,6 +98,7 @@ namespace dms::tokens { "ret", "gotoo", "jump", + "exit", "nil" }; out << "Line <" << c.line_num << ">" << codes::list[c.raw] << " " << temp1[c.type] << " \t\t " << c.name; diff --git a/DMS/utils.cpp b/DMS/utils.cpp index 60c8140..cf8f158 100644 --- a/DMS/utils.cpp +++ b/DMS/utils.cpp @@ -105,4 +105,7 @@ namespace dms::utils { if (std::string::npos != p) s.erase(p + 1); } + void wait() { + std::cin.ignore(); + } } \ No newline at end of file diff --git a/DMS/utils.h b/DMS/utils.h index ade6fb9..7eb3f3e 100644 --- a/DMS/utils.h +++ b/DMS/utils.h @@ -25,4 +25,5 @@ namespace dms::utils { bool isalpha(std::string str); bool isNum(std::string str); void trim(std::string& s); + void wait(); } \ No newline at end of file diff --git a/DMS/value.cpp b/DMS/value.cpp index 1995a34..81c616f 100644 --- a/DMS/value.cpp +++ b/DMS/value.cpp @@ -30,6 +30,15 @@ namespace dms { temp << this; return temp.str(); } + value* buildValue() { + return new value; + } + value* buildVariable(std::string str) { + value* val = new value{}; + val->set(buildString(str)); + val->type = variable; + return val; + } value* buildValue(std::string str) { value* val = new value{}; val->set(buildString(str)); diff --git a/DMS/value.h b/DMS/value.h index 7922340..eb72bff 100644 --- a/DMS/value.h +++ b/DMS/value.h @@ -2,7 +2,7 @@ #include #include #include -#include +#include namespace dms { struct dms_env; @@ -83,6 +83,8 @@ namespace dms { return out; }; }; + value* buildValue(); + value* buildVariable(std::string str); value* buildValue(std::string str); value* buildValue(double dbl); value* buildValue(int i); @@ -103,8 +105,8 @@ namespace dms { }; struct dms_env { - std::map hpart; - std::map ipart; + std::unordered_map hpart; + std::unordered_map ipart; void pushValue(value* val); void pushValue(value* ind, value* val); value* getValue(value* val);