chevron dialogue parsing
This commit is contained in:
parent
93d6673226
commit
ecad7b3817
3 changed files with 157 additions and 63 deletions
188
NouVeL/NVL.cpp
188
NouVeL/NVL.cpp
|
@ -19,6 +19,20 @@ namespace {
|
||||||
std::cout << "\t";
|
std::cout << "\t";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int query_ifstream_line_number(std::ifstream& stream)
|
||||||
|
{
|
||||||
|
std::streampos pos = stream.tellg();
|
||||||
|
stream.seekg(0); // we should not be eof
|
||||||
|
int i = 0;
|
||||||
|
char c{};
|
||||||
|
while (stream.tellg() < pos)
|
||||||
|
{
|
||||||
|
if (c == '\n')
|
||||||
|
i++;
|
||||||
|
stream.get(c);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
// facilities for scopers
|
// facilities for scopers
|
||||||
const std::map<char, char> SCOPER_MAP = {
|
const std::map<char, char> SCOPER_MAP = {
|
||||||
|
@ -86,13 +100,10 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cannot match all the copes and has read to the end of the file
|
// cannot match all the scopes and has read to the end of the file
|
||||||
if (stream.eof())
|
if (stream.eof())
|
||||||
{
|
throw std::runtime_error("Can't cope with scope at line " + std::to_string(query_ifstream_line_number(stream)));
|
||||||
std::cerr << "Can't cope with scope!" << std::endl;
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final = stream.tellg();
|
final = stream.tellg();
|
||||||
|
@ -106,6 +117,26 @@ namespace {
|
||||||
{
|
{
|
||||||
stream >> std::ws;
|
stream >> std::ws;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool only_ws_before_next_newline(std::ifstream& stream) {
|
||||||
|
if (!isspace(stream.peek()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::streampos initial = stream.tellg();
|
||||||
|
char c{};
|
||||||
|
stream.get(c);
|
||||||
|
while (c != '\n')
|
||||||
|
{
|
||||||
|
stream.get(c);
|
||||||
|
if (!isspace(c)) {
|
||||||
|
stream.seekg(initial);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.seekg(initial);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string read_sequence_name(std::ifstream& nvl)
|
std::string read_sequence_name(std::ifstream& nvl)
|
||||||
{
|
{
|
||||||
|
@ -125,7 +156,8 @@ namespace {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
void skip_comment_ouside_sequence(std::ifstream& nvl, char& c)
|
|
||||||
|
void skip_comment(std::ifstream& nvl, char& c)
|
||||||
{
|
{
|
||||||
if (nvl.peek() == ';')
|
if (nvl.peek() == ';')
|
||||||
{
|
{
|
||||||
|
@ -137,7 +169,7 @@ namespace {
|
||||||
|
|
||||||
skip_ws(nvl);
|
skip_ws(nvl);
|
||||||
|
|
||||||
skip_comment_ouside_sequence(nvl, c);
|
skip_comment(nvl, c);
|
||||||
|
|
||||||
skip_ws(nvl);
|
skip_ws(nvl);
|
||||||
}
|
}
|
||||||
|
@ -146,16 +178,73 @@ namespace {
|
||||||
|
|
||||||
namespace NVL
|
namespace NVL
|
||||||
{
|
{
|
||||||
template <bool is_parent_call, bool is_symbol>
|
void parse_Dialogue(Context old_context, std::ifstream& nvl)
|
||||||
void parse_Object(Context parent_context, std::ifstream& nvl)
|
|
||||||
{
|
{
|
||||||
Object this_object;
|
if (std::get<Call*>(old_context.Scope_Hierarchy.back())->Objects.size() != 1)
|
||||||
Context this_context = parent_context;
|
throw std::runtime_error("Failed to parse dialogue at line " + std::to_string(query_ifstream_line_number(nvl)));
|
||||||
this_context.Scope_Hierarchy.push_back(&this_object);
|
|
||||||
|
const Object say_command = Object("Say");
|
||||||
|
|
||||||
|
Object speaker = std::get<Call*>(old_context.Scope_Hierarchy.back())->Objects.front(); // Copy speaker object
|
||||||
|
|
||||||
|
Sequence* parent_sequence = std::get<Sequence*>(old_context.Scope_Hierarchy.rbegin()[1]);
|
||||||
|
// old call has never been pushed, we need to exit parse_Call right away when we finish parsing the dialogue, because this already deals with calls
|
||||||
|
|
||||||
|
char c{};
|
||||||
|
nvl.get(c); // get <
|
||||||
|
std::streampos end = scope_cope('<', nvl);
|
||||||
|
|
||||||
skip_ws(nvl);
|
skip_ws(nvl);
|
||||||
|
|
||||||
std::streampos end; // unused if object is not scoped
|
do
|
||||||
|
{
|
||||||
|
parent_sequence->Calls.push_back(Call());
|
||||||
|
parent_sequence->Calls.back().Objects.push_back(say_command);
|
||||||
|
parent_sequence->Calls.back().Objects.push_back(speaker);
|
||||||
|
|
||||||
|
std::string text;
|
||||||
|
|
||||||
|
if (nvl.peek() == '>')
|
||||||
|
break;
|
||||||
|
|
||||||
|
// im not sure if the execution order here will be problematic, seems to work ok
|
||||||
|
while (!( (c == '\n' && only_ws_before_next_newline(nvl)) || nvl.peek() == '>'))
|
||||||
|
{
|
||||||
|
nvl.get(c);
|
||||||
|
text += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (isspace(text.back()))
|
||||||
|
text.erase(text.size()-1, 1);
|
||||||
|
|
||||||
|
for (size_t i = text.find('\n'); i != std::string::npos; i = text.find('\n'))
|
||||||
|
{
|
||||||
|
int e = 0;
|
||||||
|
std::cout << text.size();
|
||||||
|
while (isspace(text.at(i + e)))
|
||||||
|
e++;
|
||||||
|
|
||||||
|
text.erase(i, e);
|
||||||
|
text.insert(i, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
parent_sequence->Calls.back().Objects.push_back(Object(text));
|
||||||
|
skip_ws(nvl);
|
||||||
|
} while (nvl.tellg() < end - static_cast<std::streampos>(1));
|
||||||
|
|
||||||
|
nvl.get(c); // get >
|
||||||
|
|
||||||
|
skip_ws(nvl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool is_parent_call, bool is_symbol>
|
||||||
|
void parse_Object(Context parent_context, std::ifstream& nvl)
|
||||||
|
{
|
||||||
|
// chevron dialogue is now handled in a separate function
|
||||||
|
Object this_object;
|
||||||
|
Context this_context = parent_context;
|
||||||
|
this_context.Scope_Hierarchy.push_back(&this_object);
|
||||||
|
|
||||||
std::variant<std::string, std::vector<Object>> content = ""; // init to empty str
|
std::variant<std::string, std::vector<Object>> content = ""; // init to empty str
|
||||||
char c{};
|
char c{};
|
||||||
|
@ -171,8 +260,9 @@ namespace NVL
|
||||||
switch (nvl.peek())
|
switch (nvl.peek())
|
||||||
{
|
{
|
||||||
case '[': // List
|
case '[': // List
|
||||||
|
{
|
||||||
nvl.get(c);
|
nvl.get(c);
|
||||||
end = scope_cope('[', nvl);
|
std::streampos end = scope_cope('[', nvl);
|
||||||
while (nvl.tellg() < end - static_cast<std::streampos>(1))
|
while (nvl.tellg() < end - static_cast<std::streampos>(1))
|
||||||
{
|
{
|
||||||
parse_Object<false, false>(this_context, nvl);
|
parse_Object<false, false>(this_context, nvl);
|
||||||
|
@ -180,18 +270,17 @@ namespace NVL
|
||||||
}
|
}
|
||||||
nvl.get(c); // skip ending scoper (']')
|
nvl.get(c); // skip ending scoper (']')
|
||||||
break;
|
break;
|
||||||
case '<': // Dialogue, not yet implemented
|
}
|
||||||
end = scope_cope('<', nvl);
|
|
||||||
break;
|
|
||||||
case '\"': // String
|
case '\"': // String
|
||||||
|
{
|
||||||
nvl.get(c);
|
nvl.get(c);
|
||||||
end = scope_cope('\"', nvl);
|
std::streampos end = scope_cope('\"', nvl);
|
||||||
while (nvl.tellg() < end - static_cast<std::streampos>(1))
|
while (nvl.tellg() < end - static_cast<std::streampos>(1))
|
||||||
{
|
{
|
||||||
nvl.get(c);
|
nvl.get(c);
|
||||||
|
|
||||||
// do not concat escaping '\' to content
|
// do not concat escaping '\' to content
|
||||||
if (c == '\\' && (nvl.peek() == '\'' || nvl.peek() == '\"'))
|
if (c == '\\' && (nvl.peek() == '\'' || nvl.peek() == '\"'))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
content = std::get<std::string>(content) + c;
|
content = std::get<std::string>(content) + c;
|
||||||
|
@ -200,17 +289,18 @@ namespace NVL
|
||||||
|
|
||||||
this_object.Value = std::get<std::string>(content);
|
this_object.Value = std::get<std::string>(content);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
|
{
|
||||||
nvl.get(c);
|
nvl.get(c);
|
||||||
while (c != ' ' && c != '\n' && c != '}' && c != ']' && c != ',')
|
while (c != ' ' && c != '\n' && c != '}' && c != ']' && c != ',')
|
||||||
{
|
{
|
||||||
content = std::get<std::string>(content) + c;
|
content = std::get<std::string>(content) + c;
|
||||||
nvl.get(c);
|
nvl.get(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ']' handled in next object parse, '}' handled in sequence parse, '\n' will be skipped somewhere with skip_ws(), where? idk
|
// ']' handled in next object parse, '}' handled in sequence parse, '\n' will be skipped somewhere with skip_ws(), where? idk
|
||||||
if (c == ']' || c == '}' || c == '\n')
|
if (c == ']' || c == '}' || c == '\n')
|
||||||
{
|
{
|
||||||
nvl.putback(c);
|
nvl.putback(c);
|
||||||
}
|
}
|
||||||
|
@ -223,7 +313,7 @@ namespace NVL
|
||||||
}
|
}
|
||||||
catch (std::exception)
|
catch (std::exception)
|
||||||
{
|
{
|
||||||
if (std::get<std::string>(content) == "true")
|
if (std::get<std::string>(content) == "true")
|
||||||
{
|
{
|
||||||
this_object.Value = true;
|
this_object.Value = true;
|
||||||
}
|
}
|
||||||
|
@ -233,7 +323,7 @@ namespace NVL
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// early return if string is empty, this should only happen if calling from an object (not a call)
|
// early return if string is empty, this should only happen if calling from an object (not a call)
|
||||||
if (str_test_every_char_not(isspace, std::get<std::string>(content)))
|
if (str_test_every_char_not(&isspace, std::get<std::string>(content)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// default case if content does not match keywords
|
// default case if content does not match keywords
|
||||||
|
@ -242,6 +332,7 @@ namespace NVL
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this_object.Is_Symbol = is_symbol;
|
this_object.Is_Symbol = is_symbol;
|
||||||
|
@ -254,6 +345,7 @@ namespace NVL
|
||||||
).push_back(this_object);
|
).push_back(this_object);
|
||||||
else // parent is not yet vector, initialize as (change from nil to) vector
|
else // parent is not yet vector, initialize as (change from nil to) vector
|
||||||
std::get<Object*>(parent_context.Scope_Hierarchy.back())->Value = std::vector<Object> { this_object };
|
std::get<Object*>(parent_context.Scope_Hierarchy.back())->Value = std::vector<Object> { this_object };
|
||||||
|
// the case for chevron dialogues are handled in the switch, there is an early return in the chevron case
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_Call(Context parent_context, std::ifstream& nvl)
|
void parse_Call(Context parent_context, std::ifstream& nvl)
|
||||||
|
@ -263,12 +355,24 @@ namespace NVL
|
||||||
Context this_context = parent_context;
|
Context this_context = parent_context;
|
||||||
this_context.Scope_Hierarchy.push_back(&this_call);
|
this_context.Scope_Hierarchy.push_back(&this_call);
|
||||||
|
|
||||||
// parse the symbol (first obj in call)
|
skip_ws(nvl);
|
||||||
parse_Object<true, true>(this_context, nvl);
|
|
||||||
|
// do not push anything if line starts with a comment
|
||||||
|
char c{};
|
||||||
|
skip_comment(nvl, c);
|
||||||
|
// early exit if sequence is empty
|
||||||
|
if (nvl.peek() == '}')
|
||||||
|
return;
|
||||||
|
|
||||||
// parse the rest
|
|
||||||
while (!(nvl.peek() == '\n' || nvl.peek() == '}'))
|
while (!(nvl.peek() == '\n' || nvl.peek() == '}'))
|
||||||
{
|
{
|
||||||
|
skip_ws(nvl);
|
||||||
|
if (nvl.peek() == '<') // dialogue! abandon current call, parse_Dialogue deals with pushing calls
|
||||||
|
{
|
||||||
|
parse_Dialogue(this_context, nvl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
parse_Object<true, false>(this_context, nvl);
|
parse_Object<true, false>(this_context, nvl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +388,7 @@ namespace NVL
|
||||||
skip_ws(nvl);
|
skip_ws(nvl);
|
||||||
|
|
||||||
char c{};
|
char c{};
|
||||||
skip_comment_ouside_sequence(nvl, c);
|
skip_comment(nvl, c);
|
||||||
|
|
||||||
this_sequence = Sequence(read_sequence_name(nvl));
|
this_sequence = Sequence(read_sequence_name(nvl));
|
||||||
|
|
||||||
|
@ -293,10 +397,7 @@ namespace NVL
|
||||||
nvl.get(c); // get {
|
nvl.get(c); // get {
|
||||||
|
|
||||||
if (c != '{')
|
if (c != '{')
|
||||||
{
|
throw std::runtime_error("Sequence parse failed at line " + std::to_string(query_ifstream_line_number(nvl)));
|
||||||
std::cerr << "Sequence parse failed!" << std::endl;
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::streampos end_pos = scope_cope(c, nvl);
|
std::streampos end_pos = scope_cope(c, nvl);
|
||||||
|
|
||||||
|
@ -314,28 +415,29 @@ namespace NVL
|
||||||
void parse_NVL(Tree& root, std::string path)
|
void parse_NVL(Tree& root, std::string path)
|
||||||
{
|
{
|
||||||
std::ifstream nvl;
|
std::ifstream nvl;
|
||||||
|
std::cout << "Reading file " << path << "..." << std::endl;
|
||||||
nvl.open(path);
|
nvl.open(path);
|
||||||
|
|
||||||
if (nvl.is_open()) {
|
if (nvl.is_open()) {
|
||||||
Context current_context;
|
Context current_context;
|
||||||
current_context.Scope_Hierarchy.push_back(&root);
|
current_context.Scope_Hierarchy.push_back(&root);
|
||||||
|
|
||||||
|
skip_ws(nvl); // just in case the file is completely empty
|
||||||
|
|
||||||
while (!nvl.eof()) {
|
while (!nvl.eof()) {
|
||||||
// parse_Sequence() already takes care of comments before a sequence
|
// parse_Sequence() already takes care of comments before a sequence
|
||||||
parse_Sequence(current_context, nvl);
|
parse_Sequence(current_context, nvl);
|
||||||
skip_ws(nvl);
|
skip_ws(nvl);
|
||||||
|
|
||||||
char c{};
|
char c{};
|
||||||
skip_comment_ouside_sequence(nvl, c);
|
skip_comment(nvl, c);
|
||||||
|
|
||||||
skip_ws(nvl);
|
skip_ws(nvl);
|
||||||
}
|
}
|
||||||
|
|
||||||
nvl.close();
|
nvl.close();
|
||||||
} else {
|
} else
|
||||||
std::cerr << "Unable to read file " << path << std::endl;
|
throw std::runtime_error("Unable to read file " + path);
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,16 +454,16 @@ namespace NVL
|
||||||
std::cout << "Nil" << std::endl;
|
std::cout << "Nil" << std::endl;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
std::cout << "float: " << std::get<float>(Value) << std::endl;
|
std::cout << "Float: " << std::get<float>(Value) << std::endl;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if (Is_Symbol)
|
if (Is_Symbol)
|
||||||
std::cout << "symbol: " << std::get<std::string>(Value) << std::endl;
|
std::cout << "Symbol: " << std::get<std::string>(Value) << std::endl;
|
||||||
else
|
else
|
||||||
std::cout << "string: " << std::get<std::string>(Value) << std::endl;
|
std::cout << "String: " << std::get<std::string>(Value) << std::endl;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
std::cout << "bool: " << std::boolalpha << std::get<bool>(Value) << std::endl;
|
std::cout << "Bool: " << std::boolalpha << std::get<bool>(Value) << std::endl;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
std::cout << "List:" << std::endl;
|
std::cout << "List:" << std::endl;
|
||||||
|
|
|
@ -26,6 +26,12 @@ namespace NVL
|
||||||
bool Is_Symbol = false;
|
bool Is_Symbol = false;
|
||||||
|
|
||||||
void Print(int indent);
|
void Print(int indent);
|
||||||
|
|
||||||
|
Object()
|
||||||
|
{}
|
||||||
|
|
||||||
|
Object(std::string n) : Value(n)
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Call
|
struct Call
|
||||||
|
|
26
test_j.nvl
26
test_j.nvl
|
@ -1,22 +1,8 @@
|
||||||
; dasjdh
|
|
||||||
; dasjdh
|
|
||||||
|
|
||||||
mmawesome {
|
mmawesome {
|
||||||
uahsdfidaf asjd
|
; testing chevron syntax
|
||||||
[][][][[[][] hi]] ; yes
|
"not mmaker" <
|
||||||
} ; dasjdh
|
I just realized that
|
||||||
|
|
||||||
; dasjdh
|
I get this for free with scope coping
|
||||||
; dasjdh
|
>
|
||||||
saddasdaesome {} ; dasjdh
|
}
|
||||||
|
|
||||||
; dasjdh
|
|
||||||
; dasjdh
|
|
||||||
|
|
||||||
; dasjdh
|
|
||||||
sadkoajsdo {
|
|
||||||
kifsdj [] [] [] [][ ] [[[[][[]]]]] asd 3.223.232 .2323 34.34 true false ;dsad
|
|
||||||
}; dasjdh
|
|
||||||
|
|
||||||
; dasjdh
|
|
||||||
; dasjdh
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue