chevron dialogue parsing

This commit is contained in:
lachrymaL 2021-05-19 17:02:57 -04:00
parent 93d6673226
commit ecad7b3817
No known key found for this signature in database
GPG key ID: F3640ACFA174B1C1
3 changed files with 157 additions and 63 deletions

View file

@ -19,6 +19,20 @@ namespace {
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
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())
{
std::cerr << "Can't cope with scope!" << std::endl;
throw;
}
throw std::runtime_error("Can't cope with scope at line " + std::to_string(query_ifstream_line_number(stream)));
}
final = stream.tellg();
@ -106,6 +117,26 @@ namespace {
{
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)
{
@ -125,7 +156,8 @@ namespace {
return token;
}
void skip_comment_ouside_sequence(std::ifstream& nvl, char& c)
void skip_comment(std::ifstream& nvl, char& c)
{
if (nvl.peek() == ';')
{
@ -137,7 +169,7 @@ namespace {
skip_ws(nvl);
skip_comment_ouside_sequence(nvl, c);
skip_comment(nvl, c);
skip_ws(nvl);
}
@ -146,16 +178,73 @@ namespace {
namespace NVL
{
template <bool is_parent_call, bool is_symbol>
void parse_Object(Context parent_context, std::ifstream& nvl)
void parse_Dialogue(Context old_context, std::ifstream& nvl)
{
Object this_object;
Context this_context = parent_context;
this_context.Scope_Hierarchy.push_back(&this_object);
if (std::get<Call*>(old_context.Scope_Hierarchy.back())->Objects.size() != 1)
throw std::runtime_error("Failed to parse dialogue at line " + std::to_string(query_ifstream_line_number(nvl)));
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);
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
char c{};
@ -171,8 +260,9 @@ namespace NVL
switch (nvl.peek())
{
case '[': // List
{
nvl.get(c);
end = scope_cope('[', nvl);
std::streampos end = scope_cope('[', nvl);
while (nvl.tellg() < end - static_cast<std::streampos>(1))
{
parse_Object<false, false>(this_context, nvl);
@ -180,18 +270,17 @@ namespace NVL
}
nvl.get(c); // skip ending scoper (']')
break;
case '<': // Dialogue, not yet implemented
end = scope_cope('<', nvl);
break;
}
case '\"': // String
{
nvl.get(c);
end = scope_cope('\"', nvl);
std::streampos end = scope_cope('\"', nvl);
while (nvl.tellg() < end - static_cast<std::streampos>(1))
{
nvl.get(c);
// do not concat escaping '\' to content
if (c == '\\' && (nvl.peek() == '\'' || nvl.peek() == '\"'))
if (c == '\\' && (nvl.peek() == '\'' || nvl.peek() == '\"'))
continue;
content = std::get<std::string>(content) + c;
@ -200,17 +289,18 @@ namespace NVL
this_object.Value = std::get<std::string>(content);
break;
}
default:
{
nvl.get(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);
}
// ']' 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);
}
@ -223,7 +313,7 @@ namespace NVL
}
catch (std::exception)
{
if (std::get<std::string>(content) == "true")
if (std::get<std::string>(content) == "true")
{
this_object.Value = true;
}
@ -233,7 +323,7 @@ namespace NVL
else
{
// 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;
// default case if content does not match keywords
@ -242,6 +332,7 @@ namespace NVL
}
break;
}
}
this_object.Is_Symbol = is_symbol;
@ -254,6 +345,7 @@ namespace NVL
).push_back(this_object);
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 };
// 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)
@ -263,12 +355,24 @@ namespace NVL
Context this_context = parent_context;
this_context.Scope_Hierarchy.push_back(&this_call);
// parse the symbol (first obj in call)
parse_Object<true, true>(this_context, nvl);
skip_ws(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() == '}'))
{
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);
}
@ -284,7 +388,7 @@ namespace NVL
skip_ws(nvl);
char c{};
skip_comment_ouside_sequence(nvl, c);
skip_comment(nvl, c);
this_sequence = Sequence(read_sequence_name(nvl));
@ -293,10 +397,7 @@ namespace NVL
nvl.get(c); // get {
if (c != '{')
{
std::cerr << "Sequence parse failed!" << std::endl;
throw;
}
throw std::runtime_error("Sequence parse failed at line " + std::to_string(query_ifstream_line_number(nvl)));
std::streampos end_pos = scope_cope(c, nvl);
@ -314,28 +415,29 @@ namespace NVL
void parse_NVL(Tree& root, std::string path)
{
std::ifstream nvl;
std::cout << "Reading file " << path << "..." << std::endl;
nvl.open(path);
if (nvl.is_open()) {
Context current_context;
current_context.Scope_Hierarchy.push_back(&root);
skip_ws(nvl); // just in case the file is completely empty
while (!nvl.eof()) {
// parse_Sequence() already takes care of comments before a sequence
parse_Sequence(current_context, nvl);
skip_ws(nvl);
char c{};
skip_comment_ouside_sequence(nvl, c);
skip_comment(nvl, c);
skip_ws(nvl);
}
nvl.close();
} else {
std::cerr << "Unable to read file " << path << std::endl;
throw;
}
} else
throw std::runtime_error("Unable to read file " + path);
}
@ -352,16 +454,16 @@ namespace NVL
std::cout << "Nil" << std::endl;
break;
case 1:
std::cout << "float: " << std::get<float>(Value) << std::endl;
std::cout << "Float: " << std::get<float>(Value) << std::endl;
break;
case 2:
if (Is_Symbol)
std::cout << "symbol: " << std::get<std::string>(Value) << std::endl;
std::cout << "Symbol: " << std::get<std::string>(Value) << std::endl;
else
std::cout << "string: " << std::get<std::string>(Value) << std::endl;
std::cout << "String: " << std::get<std::string>(Value) << std::endl;
break;
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;
case 4:
std::cout << "List:" << std::endl;

View file

@ -26,6 +26,12 @@ namespace NVL
bool Is_Symbol = false;
void Print(int indent);
Object()
{}
Object(std::string n) : Value(n)
{}
};
struct Call

View file

@ -1,22 +1,8 @@
; dasjdh
; dasjdh
mmawesome {
uahsdfidaf asjd
[][][][[[][] hi]] ; yes
} ; dasjdh
; testing chevron syntax
"not mmaker" <
I just realized that
; dasjdh
; dasjdh
saddasdaesome {} ; dasjdh
; dasjdh
; dasjdh
; dasjdh
sadkoajsdo {
kifsdj [] [] [] [][ ] [[[[][[]]]]] asd 3.223.232 .2323 34.34 true false ;dsad
}; dasjdh
; dasjdh
; dasjdh
I get this for free with scope coping
>
}