Adding new commands (NT CODE)
This commit is contained in:
parent
a0688f64dd
commit
8464b62907
@ -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" />
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
};
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
183
DMS/LineParserMatchProcess.cpp
Normal file
183
DMS/LineParserMatchProcess.cpp
Normal 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!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
@ -472,7 +48,7 @@ namespace dms {
|
||||
bool labelStart = false;
|
||||
size_t line = 1;
|
||||
while (data != NULL) {
|
||||
if (data == '/' && stream.peek()=='/') {
|
||||
if (data == '/' && stream.peek() == '/') {
|
||||
//line++;
|
||||
stream.next('\n'); // Seek until you find a newline
|
||||
}
|
||||
@ -490,7 +66,7 @@ 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 {
|
||||
@ -675,7 +255,7 @@ namespace dms {
|
||||
}
|
||||
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
195
DMS/LineParserUtils.cpp
Normal 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() {}
|
||||
}
|
||||
@ -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" };
|
||||
@ -19,8 +19,7 @@ namespace dms::codes {
|
||||
FORE,
|
||||
UNWN,
|
||||
WHLE,
|
||||
FNWR,
|
||||
FNNR,
|
||||
FUNC,
|
||||
IFFF,
|
||||
ELIF,
|
||||
ELSE,
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -105,4 +105,7 @@ namespace dms::utils {
|
||||
if (std::string::npos != p)
|
||||
s.erase(p + 1);
|
||||
}
|
||||
void wait() {
|
||||
std::cin.ignore();
|
||||
}
|
||||
}
|
||||
@ -25,4 +25,5 @@ namespace dms::utils {
|
||||
bool isalpha(std::string str);
|
||||
bool isNum(std::string str);
|
||||
void trim(std::string& s);
|
||||
void wait();
|
||||
}
|
||||
@ -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));
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user