183 lines
6.5 KiB
C++
183 lines
6.5 KiB
C++
#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!
|
|
}
|
|
}
|
|
}
|
|
} |