evaluator ig
This commit is contained in:
parent
87b9471735
commit
57c69e2db4
10 changed files with 280 additions and 149 deletions
|
@ -4,6 +4,6 @@
|
|||
cmake_minimum_required (VERSION 3.8)
|
||||
|
||||
# Add source to this project's executable.
|
||||
add_executable (NouVeL "NouVeL.cpp" "Parser.cpp" "Environment.cpp" "Environment.h")
|
||||
add_executable (NouVeL "NouVeL.cpp" "Parser.cpp" "Environment.cpp" "Environment.h" "Markup.cpp" "Common.h")
|
||||
|
||||
# TODO: Add tests and install targets if needed.
|
||||
|
|
5
NouVeL/Common.h
Normal file
5
NouVeL/Common.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
namespace NVL {
|
||||
using Number = float;
|
||||
}
|
|
@ -1,12 +1,20 @@
|
|||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <any>
|
||||
#include <variant>
|
||||
#include <stdexcept>
|
||||
#include "Environment.h"
|
||||
#include "Parser.h"
|
||||
|
||||
namespace NVL {
|
||||
const unsigned LOG_SIZE = 50;
|
||||
#include <stdexcept>
|
||||
|
||||
namespace NVL::Environment {
|
||||
Environment ENVIRONMENT;
|
||||
|
||||
Variable::Variable() {}
|
||||
Variable::Variable(Type t) : type(t) {
|
||||
if (t != Type::Nil)
|
||||
throw std::runtime_error("Cannot create non-nil object with type alone");
|
||||
}
|
||||
Variable::Variable(const Number& v) : type(Type::Number), value(v), length(1) {}
|
||||
Variable::Variable(const std::string& v) : type(Type::String), value(v), length(1) {}
|
||||
Variable::Variable(const std::vector<Variable>& v) : type(Type::Array), value(v), length(v.size()) {}
|
||||
Variable::Variable(const std::function < Variable(std::vector<Variable>)>& v, int l) : type(Type::Procedure), value(v), length(l) {}
|
||||
|
||||
bool Variable::operator==(const Variable& other) const {
|
||||
if (other.type != type)
|
||||
|
@ -20,17 +28,6 @@ namespace NVL {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
Variable::Variable() {}
|
||||
Variable::Variable(Type t) : type(t) {
|
||||
if (t != Type::Nil)
|
||||
throw std::runtime_error("Cannot create non-nil object with type alone");
|
||||
}
|
||||
Variable::Variable(const Number& v) : type(Type::Number), value(v), length(1) {}
|
||||
Variable::Variable(const std::string& v) : type(Type::String), value(v), length(1) {}
|
||||
Variable::Variable(const std::vector<Variable>& v) : type(Type::Array), value(v), length(v.size()) {}
|
||||
Variable::Variable(const std::function < Variable(std::vector<Variable>)>& v, int l) : type(Type::Procedure), value(v), length(l) {}
|
||||
|
||||
Environment ENVIRONMENT;
|
||||
|
||||
void Environment::enter(const std::string& name, Variable p) {
|
||||
if (env.find(name) != env.end())
|
||||
|
@ -44,7 +41,7 @@ namespace NVL {
|
|||
env[name] = p;
|
||||
}
|
||||
|
||||
Variable Environment::get(const std::string& name) {
|
||||
Variable& Environment::get(const std::string& name) {
|
||||
return env.at(name);
|
||||
}
|
||||
|
||||
|
@ -52,4 +49,37 @@ namespace NVL {
|
|||
return env.find(name) != env.end();
|
||||
}
|
||||
|
||||
Variable Eval(const Parse::Object& obj) {
|
||||
switch (obj.type) {
|
||||
case Parse::Type::Symbol:
|
||||
return std::get<std::string>(obj.value);
|
||||
case Parse::Type::Number:
|
||||
return Variable(std::get<Number>(obj.value));
|
||||
case Parse::Type::String:
|
||||
return Variable(std::get<std::string>(obj.value));
|
||||
case Parse::Type::Array:
|
||||
{
|
||||
std::vector<Variable> v{};
|
||||
for (const auto& x : std::get<std::vector<Parse::Object>>(obj.value))
|
||||
v.push_back(Eval(x));
|
||||
return Variable(v);
|
||||
}
|
||||
case Parse::Type::Subexpression:
|
||||
{
|
||||
return Apply(std::get<std::vector<Parse::Object>>(obj.value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Variable Apply(const Parse::Command& c) {
|
||||
std::vector<Variable> args{};
|
||||
for (int i = 1; i < c.size(); i++)
|
||||
args.push_back(Eval(c[i]));
|
||||
return std::get<std::function<Variable(std::vector<Variable>)>>(ENVIRONMENT.get(std::get<std::string>(c[0].value)).value)(args);
|
||||
}
|
||||
|
||||
void EvalScene(const Parse::Scene& s) {
|
||||
for (const auto& c : s.get())
|
||||
Apply(c);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
#pragma once
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <any>
|
||||
#include <variant>
|
||||
#include "Parser.h"
|
||||
#include "Common.h"
|
||||
|
||||
namespace NVL {
|
||||
using Number = float;
|
||||
namespace NVL::Environment {
|
||||
enum class Type { Procedure, Number, String, Array, Nil };
|
||||
|
||||
struct Variable {
|
||||
Type type;
|
||||
std::variant<Number, std::string, std::vector<Variable>, std::function<Variable(std::vector<Variable>)>> value;
|
||||
// Most things will have length 1 (including string); this is mostly for function arity and array length
|
||||
int length;
|
||||
bool operator==(const Variable& other) const;
|
||||
Variable();
|
||||
|
@ -18,7 +19,7 @@ namespace NVL {
|
|||
Variable(const Number& v);
|
||||
Variable(const std::string& v);
|
||||
Variable(const std::vector<Variable>& v);
|
||||
Variable(const std::function < Variable(std::vector<Variable>)>& v, int l);
|
||||
Variable(const std::function<Variable(std::vector<Variable>)>& v, int l);
|
||||
};
|
||||
|
||||
class Environment {
|
||||
|
@ -27,8 +28,13 @@ namespace NVL {
|
|||
public:
|
||||
void enter(const std::string& name, Variable p);
|
||||
void set(const std::string& name, Variable p);
|
||||
Variable get(const std::string& name);
|
||||
Variable& get(const std::string& name);
|
||||
bool exists(const std::string& name);
|
||||
};
|
||||
|
||||
extern Environment ENVIRONMENT;
|
||||
|
||||
Variable Eval(const Parse::Object& obj);
|
||||
Variable Apply(const Parse::Command& c);
|
||||
void EvalScene(const Parse::Scene & s);
|
||||
}
|
||||
|
|
36
NouVeL/Markup.cpp
Normal file
36
NouVeL/Markup.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#if 0
|
||||
|
||||
struct Markup {
|
||||
//... TODO
|
||||
};
|
||||
|
||||
struct MarkupInstance {
|
||||
std::pair<unsigned, unsigned> range;
|
||||
std::string effect; //TEMP
|
||||
};
|
||||
|
||||
|
||||
std::vector<int> discards;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s[i] == ESCAPE) {
|
||||
if (DIALOGUE_ESCAPED_SINGLE.accept.find(s[i]) != std::string::npos) {
|
||||
discards.push_back(i++);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Unrecognized escape sequence");
|
||||
}
|
||||
}
|
||||
std::string new_s{ s };
|
||||
for (int i = 0; i < discards.size(); i++) {
|
||||
new_s.erase(discards[i] - i, 1);
|
||||
discards[i] -= i; // New indices become the positions of the escaped characters
|
||||
}
|
||||
// TODO
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (!discards.empty() && discards[0] < i) {
|
||||
discards.erase(discards.begin());
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
@ -2,13 +2,26 @@
|
|||
#include "Environment.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
int main() {
|
||||
NVL::Variable v([](NVL::Variable) { return NVL::Variable(NVL::Type::Nil); }, 1);
|
||||
|
||||
NVL::ENVIRONMENT.enter("hello", v);
|
||||
auto f = [](NVL::Environment::Variable x) { return NVL::Environment::Variable(NVL::Environment::Type::Nil); };
|
||||
NVL::Environment::ENVIRONMENT.enter("Hello", { f, 1 });
|
||||
NVL::Environment::ENVIRONMENT.enter("Command", { f, 1 });
|
||||
NVL::Environment::ENVIRONMENT.enter("Do", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter("Set", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter("This", { f, 1 });
|
||||
NVL::Environment::ENVIRONMENT.enter("ClearDialog", { f, 0 });
|
||||
NVL::Environment::ENVIRONMENT.enter("Choice", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter("=?", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter("+", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter("switch", { f, 2 });
|
||||
NVL::Environment::ENVIRONMENT.enter("SwitchSpeaker", { f, 1 });
|
||||
NVL::Environment::ENVIRONMENT.enter("Say", { f, 1 });
|
||||
|
||||
const std::string PJ_DIR = "E:\\Archive\\Projects\\NouVeL\\NouVeL\\";
|
||||
NVL::ParseFile(PJ_DIR + "test.nvl");
|
||||
for (auto& s : NVL::Parse::ParseFile(PJ_DIR + "test.nvl")) {
|
||||
NVL::Environment::EvalScene(s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include "Parser.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
@ -27,7 +25,12 @@ namespace {
|
|||
std::string accept;
|
||||
|
||||
operator char() const {
|
||||
if (accept.length() == 1)
|
||||
return accept[0];
|
||||
else {
|
||||
std::cerr << "Cannot demote Match " << accept << " to char" << std::endl;
|
||||
return '\0';
|
||||
}
|
||||
}
|
||||
bool operator== (const std::string& other) const {
|
||||
return accept == other;
|
||||
|
@ -36,6 +39,7 @@ namespace {
|
|||
|
||||
const ParseGroup NUMERIC = { "1234567890" };
|
||||
const Match DECIMAL_DOT = { "." };
|
||||
const Match NEGATIVE = { "-" };
|
||||
const ParseGroup ALPHA = { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" };
|
||||
const Match ARRAY_OPEN = { "[" };
|
||||
const Match ARRAY_CLOSE = { "]" };
|
||||
|
@ -48,7 +52,18 @@ namespace {
|
|||
const Match DIALOGUE_CLOSE = { "->>" };
|
||||
const Match BEGIN = { "BEGIN" };
|
||||
const Match END = { "END" };
|
||||
const ParseGroup SYMBOL = { ALPHA.accept + NUMERIC.accept + "_-" };
|
||||
const ParseGroup SYMBOL = { ALPHA.accept + NUMERIC.accept + "_"};
|
||||
const Match SPECIAL_SYMBOLS[] = {
|
||||
{ "+" },
|
||||
{ "-" },
|
||||
{ "*" },
|
||||
{ "/" },
|
||||
{ "=?" },
|
||||
{ ">?" },
|
||||
{ "<?" },
|
||||
{ "<=?" },
|
||||
{ ">=?" }
|
||||
};
|
||||
const ParseGroup WS = { " \t\v\f\r\n" };
|
||||
const ParseGroup SEPARATOR = {
|
||||
WS.accept +
|
||||
|
@ -57,7 +72,7 @@ namespace {
|
|||
char(GROUP_OPEN) +
|
||||
char(GROUP_CLOSE) +
|
||||
char(ARRAY_DELIM) +
|
||||
char(COMMENT_BEGIN)
|
||||
COMMENT_BEGIN.accept[0]
|
||||
};
|
||||
const Match NEWLINE = { "\n" };
|
||||
const ParseGroup ESCAPED = { "\\\"" };
|
||||
|
@ -89,8 +104,6 @@ namespace {
|
|||
// char(TEMPLATE_CLOSE)
|
||||
};
|
||||
|
||||
enum class ParseType { Symbol, Number, String, Array, Subexpression };
|
||||
|
||||
std::string read_file_to_string(const std::string& path) {
|
||||
std::ifstream f(path);
|
||||
{ // Some apps on Windows adds this signature in front of UTF-8 files when saving
|
||||
|
@ -120,8 +133,10 @@ namespace {
|
|||
}
|
||||
|
||||
inline bool IsNumeric(const std::string& str) {
|
||||
bool negative = str[0] == NEGATIVE;
|
||||
bool had_dot = false;
|
||||
for (auto& c : str) {
|
||||
|
||||
for (auto& c : negative ? str.substr(1) : str) {
|
||||
if (NUMERIC.accept.find(c) == std::string::npos) {
|
||||
if (c == DECIMAL_DOT.accept[0]) {
|
||||
if (had_dot)
|
||||
|
@ -133,38 +148,20 @@ namespace {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (had_dot + negative == str.length())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class Object {
|
||||
public:
|
||||
ParseType type;
|
||||
std::variant<
|
||||
NVL::Number,
|
||||
std::string,
|
||||
std::vector<Object>
|
||||
> value;
|
||||
};
|
||||
using Command = std::vector<Object>;
|
||||
|
||||
class Scene {
|
||||
std::string name;
|
||||
std::vector<Command> commands{};
|
||||
public:
|
||||
Scene(const std::string& name) : name(name) {}
|
||||
void append(const Command& c) {
|
||||
commands.push_back(c);
|
||||
inline bool ContainsOnlyWS(const std::string& s) {
|
||||
for (auto& c : s) {
|
||||
if (WS.accept.find(c) == std::string::npos)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct Markup {
|
||||
//... TODO
|
||||
};
|
||||
|
||||
struct MarkupInstance {
|
||||
std::pair<unsigned, unsigned> range;
|
||||
std::string effect; //TEMP
|
||||
};
|
||||
|
||||
void SkipWS(const std::string& f, size_t& pos) {
|
||||
while (WS.accept.find(f[pos]) != std::string::npos)
|
||||
|
@ -196,7 +193,7 @@ namespace {
|
|||
while (++pos) {
|
||||
if (SEPARATOR.accept.find(f[pos]) != std::string::npos)
|
||||
break;
|
||||
};
|
||||
}
|
||||
return f.substr(start, pos - start);
|
||||
}
|
||||
|
||||
|
@ -206,11 +203,15 @@ namespace {
|
|||
while (++pos) {
|
||||
if (SEPARATOR.accept.find(f[pos]) != std::string::npos)
|
||||
break;
|
||||
};
|
||||
}
|
||||
return f.substr(start, pos - start);
|
||||
}
|
||||
|
||||
bool IsLegalSymbolName(const std::string& token) {
|
||||
for (const auto& x: SPECIAL_SYMBOLS) {
|
||||
if (token == x.accept)
|
||||
return true;
|
||||
}
|
||||
if (ALPHA.accept.find(token[0]) == std::string::npos)
|
||||
return false;
|
||||
for (auto& i : token)
|
||||
|
@ -219,11 +220,11 @@ namespace {
|
|||
return true;
|
||||
}
|
||||
|
||||
Object ParseExpression(const std::string& f, size_t& pos);
|
||||
Object ParseArray(const std::string& f, size_t& pos, int layer) {
|
||||
NVL::Parse::Object ParseExpression(const std::string& f, size_t& pos);
|
||||
NVL::Parse::Object ParseArray(const std::string& f, size_t& pos, int layer) {
|
||||
SkipComments(f, pos);
|
||||
|
||||
std::vector<Object> array{};
|
||||
std::vector<NVL::Parse::Object> array{};
|
||||
|
||||
array.push_back(ParseExpression(f, pos));
|
||||
while (PeekToken(f, pos)[0] != ARRAY_CLOSE) {
|
||||
|
@ -234,7 +235,7 @@ namespace {
|
|||
array.push_back(ParseExpression(f, pos));
|
||||
}
|
||||
|
||||
return { ParseType::Array, array };
|
||||
return { NVL::Parse::Type::Array, array };
|
||||
}
|
||||
|
||||
std::string ParseString(const std::string& f, size_t& pos) {
|
||||
|
@ -263,16 +264,16 @@ namespace {
|
|||
return str;
|
||||
}
|
||||
unsigned GetProcedureArity(const std::string& key) {
|
||||
return NVL::ENVIRONMENT.get(key).length;
|
||||
return NVL::Environment::ENVIRONMENT.get(key).length;
|
||||
}
|
||||
|
||||
Command ParseCommand(const std::string& f, size_t& pos) {
|
||||
NVL::Parse::Command ParseCommand(const std::string& f, size_t& pos) {
|
||||
SkipComments(f, pos);
|
||||
|
||||
auto proc = GetToken(f, pos);
|
||||
|
||||
if (!IsLegalSymbolName(proc)) throw std::runtime_error("Illegal Procedure name");
|
||||
Command c{ Object{ ParseType::Symbol, proc } };
|
||||
NVL::Parse::Command c{ NVL::Parse::Object{ NVL::Parse::Type::Symbol, proc } };
|
||||
for (int i = 0; i < GetProcedureArity(proc); i++) {
|
||||
c.push_back(ParseExpression(f, pos));
|
||||
};
|
||||
|
@ -281,7 +282,7 @@ namespace {
|
|||
return c;
|
||||
}
|
||||
|
||||
Object ParseExpression(const std::string& f, size_t& pos) {
|
||||
NVL::Parse::Object ParseExpression(const std::string& f, size_t& pos) {
|
||||
SkipComments(f, pos);
|
||||
|
||||
auto t = PeekToken(f, pos);
|
||||
|
@ -301,70 +302,49 @@ namespace {
|
|||
throw std::runtime_error("Cannot match closing subexpression");
|
||||
else
|
||||
SkipOverFirstChar(f, pos);
|
||||
return Object{ ParseType::Subexpression, c };
|
||||
return NVL::Parse::Object{ NVL::Parse::Type::Subexpression, c };
|
||||
}
|
||||
else if (t[0] == GROUP_CLOSE)
|
||||
throw std::runtime_error("Cannot match closing subexpression, likely too few arguments");
|
||||
else if (t[0] == QUOTE)
|
||||
return { ParseType::String, ParseString(f, pos) };
|
||||
return { NVL::Parse::Type::String, ParseString(f, pos) };
|
||||
else if (t[0] == ARRAY_CLOSE)
|
||||
throw std::runtime_error("Cannot match closing array");
|
||||
else {
|
||||
auto token = GetToken(f, pos);
|
||||
if (IsNumeric(token))
|
||||
return { ParseType::Number, std::stof(token) };
|
||||
return { NVL::Parse::Type::Number, std::stof(token) };
|
||||
else if (IsLegalSymbolName(token))
|
||||
return { ParseType::Symbol, token };
|
||||
return { NVL::Parse::Type::Symbol, token };
|
||||
else
|
||||
throw std::runtime_error("Illegal symbol");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Command ParseDialogue(const std::string& s) {
|
||||
NVL::Parse::Command ParseDialogue(const std::string& s) {
|
||||
if (s.substr(0, 2) == COMMAND_ESCAPE.accept) {
|
||||
size_t dummy = 0;
|
||||
return ParseCommand(s.substr(2), dummy); // TODO string[] will throw on this 100%, maybe do ParseDialogueCommand
|
||||
// Pad a space towards the end, the helpers do not expect strings to immediately terminate
|
||||
return ParseCommand(s.substr(2) + " ", dummy);
|
||||
}
|
||||
|
||||
// assume SwitchSpeaker and Say are unary for now
|
||||
|
||||
if (s.back() == SPEAKER_CLOSE) {
|
||||
if (s.front() == SPEAKER_OPEN) {
|
||||
auto name = s.substr(1, s.length() - 2);
|
||||
if (IsLegalSymbolName(name))
|
||||
return { { ParseType::Symbol, "SwitchSpeaker" }, { ParseType::String, name } };
|
||||
return { { NVL::Parse::Type::Symbol, "SwitchSpeaker" }, { NVL::Parse::Type::String, name } };
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Malformed speaker command");
|
||||
}
|
||||
|
||||
std::vector<int> discards;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (s[i] == ESCAPE) {
|
||||
if (DIALOGUE_ESCAPED_SINGLE.accept.find(s[i]) != std::string::npos) {
|
||||
discards.push_back(i++);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Unrecognized escape sequence");
|
||||
}
|
||||
}
|
||||
std::string new_s{s};
|
||||
for (int i = 0; i < discards.size(); i++) {
|
||||
new_s.erase(discards[i] - i, 1);
|
||||
discards[i] -= i; // New indices become the positions of the escaped characters
|
||||
}
|
||||
// TODO
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (!discards.empty() && discards[0] < i) {
|
||||
discards.erase(discards.begin());
|
||||
return { { NVL::Parse::Type::Symbol, "Say" }, { NVL::Parse::Type::String, s } };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::cout << new_s << std::endl;
|
||||
return { { ParseType::Symbol, "Say" }, { ParseType::String, new_s } };
|
||||
}
|
||||
|
||||
Scene ParseScene(const std::string& f, size_t& pos) {
|
||||
NVL::Parse::Scene ParseScene(const std::string& f, size_t& pos) {
|
||||
SkipComments(f, pos);
|
||||
|
||||
if (!(GetToken(f, pos) == BEGIN.accept))
|
||||
|
@ -372,7 +352,7 @@ namespace {
|
|||
|
||||
auto scene_name = GetToken(f, pos);
|
||||
if (!IsLegalSymbolName(scene_name)) throw std::runtime_error("Illegal Scene name");
|
||||
Scene s{ scene_name };
|
||||
NVL::Parse::Scene s{ scene_name };
|
||||
|
||||
bool dialogue_mode = false;
|
||||
|
||||
|
@ -391,6 +371,7 @@ namespace {
|
|||
throw std::runtime_error("Dialogue does not terminate");
|
||||
auto lines = split_string_by_lines(f.substr(pos, end - pos));
|
||||
for (auto& l : lines) {
|
||||
if (!l.empty() && !ContainsOnlyWS(l))
|
||||
s.append(ParseDialogue(l));
|
||||
}
|
||||
dialogue_mode = false;
|
||||
|
@ -405,43 +386,13 @@ namespace {
|
|||
|
||||
GetToken(f, pos); // skip END
|
||||
|
||||
SkipComments(f, pos);
|
||||
return s;
|
||||
}
|
||||
|
||||
/*Scene EvalScene(const std::vector<std::vector<Parse>>& parses) { // TODO THIS IS TOTALLY FUCKED
|
||||
if (parses[0].size() != 2)
|
||||
throw std::runtime_error("Too many tokens in Scene BEGIN");
|
||||
|
||||
Scene r(parses[0][1].parse);
|
||||
for (int i = 1; i < parses.size() - 1; i++) { // Skip first and last lines, presumed BEGIN and END
|
||||
if (!ENV.exists(parses[i][0].parse))
|
||||
throw std::runtime_error("Symbol not defined in environment");
|
||||
Command call = { { Type::Symbol, parses[i][0].parse }, {} };
|
||||
for (int token_i = 1; token_i < parses[i].size();) { // skip first token
|
||||
switch (parses[i][token_i].type) {
|
||||
case Type::Symbol:
|
||||
{
|
||||
|
||||
}
|
||||
case Type::Number:
|
||||
call.args.push_back(Object{ Type::Number, std::stof(parses[i][token_i].parse) });
|
||||
break;
|
||||
case Type::String:
|
||||
call.args.push_back(Object{ Type::String, parses[i][token_i].parse });
|
||||
break;
|
||||
case Type::Array:
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
namespace NVL {
|
||||
void ParseFile(const std::string& path) {
|
||||
namespace NVL::Parse {
|
||||
std::vector<Scene> ParseFile(const std::string& path) {
|
||||
std::string f = read_file_to_string(path);
|
||||
|
||||
std::vector<Scene> list {}; // Vector of scenes which each contain a vector of Parses
|
||||
|
@ -449,6 +400,7 @@ namespace NVL {
|
|||
list.push_back(ParseScene(f, i));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,39 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <string>
|
||||
#include "Common.h"
|
||||
|
||||
namespace NVL {
|
||||
void ParseFile(const std::string& path);
|
||||
namespace NVL::Parse {
|
||||
enum class Type { Symbol, Number, String, Array, Subexpression };
|
||||
|
||||
class Object {
|
||||
public:
|
||||
Type type;
|
||||
std::variant<
|
||||
Number,
|
||||
std::string,
|
||||
std::vector<Object>
|
||||
> value;
|
||||
};
|
||||
|
||||
using Command = std::vector<Object>;
|
||||
|
||||
class Scene {
|
||||
std::string name;
|
||||
std::vector<Command> commands{};
|
||||
public:
|
||||
Scene(const std::string& name) : name(name) {}
|
||||
void append(const Command& c) {
|
||||
commands.push_back(c);
|
||||
}
|
||||
void append(Command&& c) {
|
||||
commands.push_back(std::move(c));
|
||||
}
|
||||
const auto& get() const {
|
||||
return commands;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<Scene> ParseFile(const std::string& path);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ Set var3 (+ var var2)
|
|||
|
||||
// Enter and exit dialogue mode with <<- and ->>
|
||||
<<-
|
||||
// Comments are only legal at the beginning like this in Dialogue mode
|
||||
// IDEA: once NVL is parsed, set up indices for each line
|
||||
// Make another program to match audio
|
||||
[Alex]
|
||||
|
@ -44,7 +45,8 @@ To grab a value from the environment, do it like this: ${var}.
|
|||
This is also the syntax to evaluate a command from dialogue mode.
|
||||
If the return is void it will say "undefined" or something like that.
|
||||
|
||||
*! This "is a command in dialogue mode." // Dialogue mode commands have to be one liners
|
||||
// Dialogue mode commands have to be one liners an cannot have trailing comments (arbitrary)
|
||||
*! This "is a command in dialogue mode."
|
||||
*! Set var (+ var 1)
|
||||
|
||||
[NARRATION]
|
||||
|
|
|
@ -1,13 +1,67 @@
|
|||
BEGIN Scene1
|
||||
// This new syntax should be easier to parse although parsing wasn't really the issue with the last syntax
|
||||
// I really want the syntax to feel a lot more like the actual script for a play or something of the sort
|
||||
|
||||
// THINK ABOUT THIS Newlines are the end of a statement in this syntax, I think this mirrors text formatting pretty well
|
||||
|
||||
// I find that the "BEGIN" things in some older languages are actually quite fitting
|
||||
BEGIN Scene1
|
||||
|
||||
Command "test"
|
||||
Do 5 [2, 3, 6]
|
||||
|
||||
Set var 2
|
||||
|
||||
// Make the actual commands like Lisp, not the entire language
|
||||
// strip the outermost brackets since this syntax will be parsed line by line
|
||||
Set var2 (=? var 1)
|
||||
Set var3 (+ var var2)
|
||||
|
||||
// Enter and exit dialogue mode with <<- and ->>
|
||||
<<-
|
||||
// Comments are only legal at the beginning like this in Dialogue mode
|
||||
// IDEA: once NVL is parsed, set up indices for each line
|
||||
// Make another program to match audio
|
||||
[Alex]
|
||||
Hello. Welcome to dialogue mode.
|
||||
Every new line is a new "click". I discarded the "pause" idea.
|
||||
|
||||
[Bailey]
|
||||
Je ne suis plus Alex.
|
||||
Ça ne m'arrête pas de vous démontrer notre support multilingue noblement distingué.
|
||||
|
||||
[Catherine]
|
||||
CJKテスト!
|
||||
夏はマシンガン。
|
||||
|
||||
[Dick]
|
||||
I present my idea for markup syntax here.
|
||||
It is something like [b,i]{this}.
|
||||
It's kind of like KP's TyperTags, only that you can specify more than one effect in the square brackets in front.
|
||||
If the effect is parametrized in some way invoke it like [wiggle_y(5)]{this}.
|
||||
Then naturally we would have ruby like [rb("that")]{this} (important feature, trust me).
|
||||
|
||||
[Elliot]
|
||||
To grab a value from the environment, do it like this: ${var}.
|
||||
This is also the syntax to evaluate a command from dialogue mode.
|
||||
If the return is void it will say "undefined" or something like that.
|
||||
|
||||
*! This "is a command in dialogue mode."
|
||||
*! Set var (+ var 1)
|
||||
|
||||
[NARRATION]
|
||||
Thanks!
|
||||
|
||||
*! ClearDialog
|
||||
|
||||
->>
|
||||
|
||||
// something like Choice can load the index of the choice the user selects into a variable
|
||||
Choice var4 ["Apple", "Orange"]
|
||||
|
||||
// After thinking about this for a while I think it would be a good idea
|
||||
// to have ways to terminate a scene other than END, for instance JUMP can be an alternative like this:
|
||||
// JUMP Scene2
|
||||
// we can equally do something like this
|
||||
// JUMP (switch var4 [[0, RouteA], [1, RouteB], [default, RouteC]])
|
||||
|
||||
END
|
||||
|
|
Loading…
Add table
Reference in a new issue