Adding new commands (NT CODE)

This commit is contained in:
Ryan Ward 2020-08-28 11:23:19 -04:00
parent a0688f64dd
commit 8464b62907
16 changed files with 665 additions and 536 deletions

View File

@ -147,10 +147,11 @@
<ClCompile Include="codes.cpp" />
<ClCompile Include="DMS.cpp" />
<ClCompile Include="dms_state.cpp" />
<ClCompile Include="LineParserExtended.cpp" />
<ClCompile Include="LineParserMatchProcess.cpp" />
<ClCompile Include="LineParserParse.cpp" />
<ClCompile Include="string_utils.cpp" />
<ClCompile Include="dms_exceptions.cpp" />
<ClCompile Include="LineParser.cpp" />
<ClCompile Include="LineParserUtils.cpp" />
<ClCompile Include="number_utils.cpp" />
<ClCompile Include="utils.cpp" />
<ClCompile Include="value.cpp" />

View File

@ -30,9 +30,6 @@
<ClCompile Include="dms_exceptions.cpp">
<Filter>Source Files\DMS</Filter>
</ClCompile>
<ClCompile Include="LineParser.cpp">
<Filter>Source Files\DMS</Filter>
</ClCompile>
<ClCompile Include="number_utils.cpp">
<Filter>Source Files\DMS</Filter>
</ClCompile>
@ -54,7 +51,13 @@
<ClCompile Include="chunk.cpp">
<Filter>Source Files\DMS</Filter>
</ClCompile>
<ClCompile Include="LineParserExtended.cpp">
<ClCompile Include="LineParserMatchProcess.cpp">
<Filter>Source Files\DMS</Filter>
</ClCompile>
<ClCompile Include="LineParserParse.cpp">
<Filter>Source Files\DMS</Filter>
</ClCompile>
<ClCompile Include="LineParserUtils.cpp">
<Filter>Source Files\DMS</Filter>
</ClCompile>
</ItemGroup>

View File

@ -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<tokens::token> temp;
size_t tabs = 0;
dms_state* state;
void doCheck(passer* stream, std::vector<tokens::token>* t_vec, size_t line, bool& isNum, bool& hasDec, std::vector<uint8_t>* 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<tokens::token> &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<tokens::token> &tok);
};
}

View File

@ -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;
}
}

View File

@ -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;
}
/// <summary>
/// Recursively parse through function related tokens
/// </summary>
/// <param name="stream"></param>
/// <param name="v"></param>
/// <returns></returns>
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 <nil> "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<token> 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!
}
}
}
}

View File

@ -3,430 +3,6 @@
using namespace dms::tokens;
using namespace dms::utils;
namespace dms {
void tokenstream::init(std::vector<token>* 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<token> tokenstream::next(tokentype to, tokentype tc) {
std::vector<token> 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<token> tokenstream::next(tokentype tk) {
std::vector<token> 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<uint8_t> 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<token>* t_vec, size_t line, bool &isNum, bool &hasDec, std::vector<uint8_t>* 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 <FLAG IDENTIFIER> " << " 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<token> 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<token> 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<token> &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<std::string, chunk> chunks;
std::unordered_map<std::string, chunk> chunks;
std::vector<token> 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 <FLAG IDENTIFIER> " << " 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<token> 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);
}
}

195
DMS/LineParserUtils.cpp Normal file
View File

@ -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<token>* 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<token> tokenstream::next(tokentype to, tokentype tc) {
std::vector<token> 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<token> tokenstream::next(tokentype tk) {
std::vector<token> 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<uint8_t> 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<token>* t_vec, size_t line, bool& isNum, bool& hasDec, std::vector<uint8_t>* 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<token> &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() {}
}

View File

@ -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" };
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" };

View File

@ -19,8 +19,7 @@ namespace dms::codes {
FORE,
UNWN,
WHLE,
FNWR,
FNNR,
FUNC,
IFFF,
ELIF,
ELSE,

View File

@ -3,6 +3,7 @@
#include "chunk.h"
#include <string>
#include <iostream>
#include <unordered_map>
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<std::string, chunk*> chunks;
std::unordered_map<std::string, chunk*> chunks;
std::string entry = "start";
std::map<std::string, bool> enables;
std::unordered_map<std::string, bool> enables;
};
}

View File

@ -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"

View File

@ -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;

View File

@ -105,4 +105,7 @@ namespace dms::utils {
if (std::string::npos != p)
s.erase(p + 1);
}
void wait() {
std::cin.ignore();
}
}

View File

@ -25,4 +25,5 @@ namespace dms::utils {
bool isalpha(std::string str);
bool isNum(std::string str);
void trim(std::string& s);
void wait();
}

View File

@ -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));

View File

@ -2,7 +2,7 @@
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <unordered_map>
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<std::string, value*> hpart;
std::map<double, value*> ipart;
std::unordered_map<std::string, value*> hpart;
std::unordered_map<double, value*> ipart;
void pushValue(value* val);
void pushValue(value* ind, value* val);
value* getValue(value* val);