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

View file

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

View file

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