ITS WORKING HOLY SHIT
This commit is contained in:
parent
e182ffc14a
commit
084acbba1e
9 changed files with 237 additions and 131 deletions
|
@ -4,7 +4,7 @@
|
|||
"name": "x64-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [ "clang_cl_x64_x64" ],
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
cmake_minimum_required (VERSION 3.8)
|
||||
|
||||
# Add source to this project's executable.
|
||||
add_executable (NouVeL "NouVeL.cpp" "NVL.cpp")
|
||||
add_executable (NouVeL "NouVeL.cpp" "Parser.cpp" "Environment.cpp" "Environment.h")
|
||||
|
||||
# TODO: Add tests and install targets if needed.
|
||||
|
|
51
NouVeL/Environment.cpp
Normal file
51
NouVeL/Environment.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <any>
|
||||
#include <variant>
|
||||
#include <stdexcept>
|
||||
#include "Environment.h"
|
||||
|
||||
namespace NVL {
|
||||
const unsigned LOG_SIZE = 50;
|
||||
|
||||
bool Variable::operator==(const Variable& other) const {
|
||||
if (other.type != type)
|
||||
return false;
|
||||
switch (type) {
|
||||
case Type::Number:
|
||||
return std::get<Number>(other.value) == std::get<Number>(value);
|
||||
case Type::String:
|
||||
return std::get<std::string>(other.value) == std::get<std::string>(value);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Variable::Variable() {}
|
||||
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(void* 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())
|
||||
throw std::runtime_error("Redefinition of symbol in environment");
|
||||
env[name] = p;
|
||||
}
|
||||
|
||||
void Environment::set(const std::string& name, Variable p) {
|
||||
if (env.find(name) == env.end())
|
||||
throw std::runtime_error("Attempting to set undefined variable");
|
||||
env[name] = p;
|
||||
}
|
||||
|
||||
Variable Environment::get(const std::string& name) {
|
||||
return env.at(name);
|
||||
}
|
||||
|
||||
bool Environment::exists(const std::string& name) {
|
||||
return env.find(name) != env.end();
|
||||
}
|
||||
|
||||
}
|
33
NouVeL/Environment.h
Normal file
33
NouVeL/Environment.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <any>
|
||||
#include <variant>
|
||||
|
||||
namespace NVL {
|
||||
using Number = float;
|
||||
enum class Type { Procedure, Number, String, Array };
|
||||
|
||||
struct Variable {
|
||||
Type type;
|
||||
std::variant<Number, std::string, std::vector<Variable>, void*> value;
|
||||
int length;
|
||||
bool operator==(const Variable& other) const;
|
||||
Variable();
|
||||
Variable(const Number& v);
|
||||
Variable(const std::string& v);
|
||||
Variable(const std::vector<Variable>& v);
|
||||
Variable(void* v, int l);
|
||||
};
|
||||
|
||||
class Environment {
|
||||
private:
|
||||
std::unordered_map<std::string, Variable> env{};
|
||||
public:
|
||||
void enter(const std::string& name, Variable p);
|
||||
void set(const std::string& name, Variable p);
|
||||
Variable get(const std::string& name);
|
||||
bool exists(const std::string& name);
|
||||
};
|
||||
extern Environment ENVIRONMENT;
|
||||
}
|
|
@ -1,8 +1,16 @@
|
|||
#include "NVL.h"
|
||||
#include "Parser.h"
|
||||
#include "Environment.h"
|
||||
#include <functional>
|
||||
#include <any>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
auto f = [](std::string& what) -> std::any { return true; };
|
||||
NVL::Variable v(&f, 3);
|
||||
|
||||
NVL::ENVIRONMENT.enter("hello", v);
|
||||
|
||||
const std::string PJ_DIR = "E:\\Archive\\Projects\\NouVeL\\NouVeL\\";
|
||||
NVL::ParseFile(PJ_DIR + "test.nvl");
|
||||
return 0;
|
||||
|
|
|
@ -1,23 +1,26 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "SymbolConfig.h"
|
||||
|
||||
|
||||
const unsigned LOG_SIZE = 50;
|
||||
#include "Environment.h"
|
||||
|
||||
namespace {
|
||||
struct ParseGroup {
|
||||
std::string accept;
|
||||
|
||||
operator std::string() const {
|
||||
return accept;
|
||||
}
|
||||
constexpr bool operator== (const std::string& other) const {
|
||||
return accept == other;
|
||||
}
|
||||
};
|
||||
|
||||
struct Match {
|
||||
|
@ -48,13 +51,19 @@ namespace {
|
|||
const ParseGroup SYMBOL = { ALPHA.accept + NUMERIC.accept + "_-" };
|
||||
const ParseGroup WS = { " \t\v\f\r\n" };
|
||||
const ParseGroup SEPARATOR = {
|
||||
WS.accept + char(ARRAY_OPEN) + char(ARRAY_CLOSE) + char(GROUP_OPEN) + char(GROUP_CLOSE)
|
||||
WS.accept +
|
||||
char(ARRAY_OPEN) +
|
||||
char(ARRAY_CLOSE) +
|
||||
char(GROUP_OPEN) +
|
||||
char(GROUP_CLOSE) +
|
||||
char(ARRAY_DELIM) +
|
||||
char(COMMENT_BEGIN)
|
||||
};
|
||||
const Match NEWLINE = { "\n" };
|
||||
const Match ESCAPE = { "\\" };
|
||||
const ParseGroup ESCAPED = { "\\\"" };
|
||||
|
||||
enum class Type { Symbol, Number, String, Array };
|
||||
enum class ParseType { Symbol, Number, String, Array, Subexpression };
|
||||
|
||||
std::string read_file_to_string(const std::string& path) {
|
||||
std::ifstream f(path);
|
||||
|
@ -67,8 +76,7 @@ namespace {
|
|||
std::vector<std::string> lines;
|
||||
int pos = 0;
|
||||
int prev = 0;
|
||||
while ((pos = str.find(NEWLINE.accept[0], prev)) != std::string::npos)
|
||||
{
|
||||
while ((pos = str.find(NEWLINE, prev)) != std::string::npos) {
|
||||
lines.push_back(str.substr(prev, pos - prev));
|
||||
prev = pos + 1;
|
||||
}
|
||||
|
@ -94,56 +102,28 @@ namespace {
|
|||
return true;
|
||||
}
|
||||
|
||||
enum class ParseState { Root, Scene, Command, Dialogue, String, Array };
|
||||
|
||||
using NVL_Number = float;
|
||||
|
||||
struct Environment {
|
||||
private:
|
||||
std::unordered_map<std::string, void*> env;
|
||||
public:
|
||||
void enter(const std::string& name, void* p) {
|
||||
if (env.find(name) != env.end())
|
||||
throw std::runtime_error("Redefinition of symbol in environment");
|
||||
env[name] = p;
|
||||
}
|
||||
|
||||
void set(const std::string& name, void* p) {
|
||||
if (env.find(name) == env.end())
|
||||
throw std::runtime_error("Attempting to set undefined variable");
|
||||
env[name] = p;
|
||||
}
|
||||
|
||||
auto get(const std::string& name) {
|
||||
return env.at(name);
|
||||
}
|
||||
|
||||
bool exists(const std::string& name) {
|
||||
return env.find(name) != env.end();
|
||||
}
|
||||
} ENV;
|
||||
|
||||
/*struct Command {
|
||||
Object procedure;
|
||||
std::vector<Object> args{};
|
||||
};*/
|
||||
class Object {
|
||||
public:
|
||||
Type type;
|
||||
ParseType type;
|
||||
std::variant<
|
||||
NVL_Number,
|
||||
NVL::Number,
|
||||
std::string,
|
||||
std::vector<Object>
|
||||
> value;
|
||||
};
|
||||
using Command = std::vector<Object>;
|
||||
|
||||
struct Command {
|
||||
Object procedure;
|
||||
std::vector<Object> args{};
|
||||
};
|
||||
|
||||
class Scene {
|
||||
std::string name;
|
||||
std::vector<Command> commands{};
|
||||
public:
|
||||
Scene(const std::string& name) : name(name) {
|
||||
ENV.enter(name, this);
|
||||
//NVL::ENVIRONMENT.enter(name, this);
|
||||
}
|
||||
void append(const Command& c) {
|
||||
commands.push_back(c);
|
||||
|
@ -169,6 +149,11 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
void SkipOverFirstChar(const std::string& f, size_t& pos) {
|
||||
SkipWS(f, pos);
|
||||
pos++;
|
||||
}
|
||||
|
||||
std::string GetToken(const std::string& f, size_t& pos) {
|
||||
SkipWS(f, pos);
|
||||
auto start = pos;
|
||||
|
@ -189,7 +174,7 @@ namespace {
|
|||
return f.substr(start, pos - start);
|
||||
}
|
||||
|
||||
bool IsSymbol(const std::string& token) {
|
||||
bool IsLegalSymbolName(const std::string& token) {
|
||||
if (ALPHA.accept.find(token[0]) == std::string::npos)
|
||||
return false;
|
||||
for (auto& i : token)
|
||||
|
@ -197,47 +182,41 @@ namespace {
|
|||
return false;
|
||||
return true;
|
||||
}
|
||||
void ParseArray(ParseState& state, const std::string& f, size_t& pos, Command& command) {
|
||||
SkipWS(f, pos);
|
||||
/*
|
||||
// Read until array end OR end of line, state wont change if array end was not found
|
||||
auto start = pos;
|
||||
while (pos < line.length()) {
|
||||
if (ARRAY_OPEN.accept[0] == line[pos])
|
||||
parse.layer++;
|
||||
else if (ARRAY_CLOSE.accept[0] == line[pos])
|
||||
break;
|
||||
pos++;
|
||||
}
|
||||
parse.parse = line.substr(start, pos++ - start);
|
||||
|
||||
if (parse.parse.back() == ARRAY_CLOSE.accept[0]) {
|
||||
parse.layer--;
|
||||
if (parse.layer == 0) {
|
||||
Object ParseExpression(const std::string& f, size_t& pos);
|
||||
Object ParseArray(const std::string& f, size_t& pos, int layer) {
|
||||
SkipComments(f, pos);
|
||||
|
||||
}
|
||||
std::vector<Object> array{};
|
||||
|
||||
array.push_back(ParseExpression(f, pos));
|
||||
while (PeekToken(f, pos)[0] != ARRAY_CLOSE) {
|
||||
if (PeekToken(f, pos)[0] == ARRAY_DELIM)
|
||||
SkipOverFirstChar(f, pos);
|
||||
else
|
||||
throw std::runtime_error("Invalid array member");
|
||||
array.push_back(ParseExpression(f, pos));
|
||||
}
|
||||
|
||||
return;
|
||||
*/
|
||||
return { ParseType::Array, array };
|
||||
}
|
||||
|
||||
std::string ParseString(ParseState& state, const std::string& f, size_t& pos) {
|
||||
SkipWS(f, pos);
|
||||
std::string ParseString(const std::string& f, size_t& pos) {
|
||||
SkipComments(f, pos);
|
||||
std::vector<int> discards{};
|
||||
auto start = ++pos; // skip opening quote
|
||||
do {
|
||||
if (f[pos] == char(QUOTE)) {
|
||||
if (f[pos] == QUOTE) {
|
||||
break;
|
||||
}
|
||||
else if (f[pos] == char(ESCAPE)) {
|
||||
else if (f[pos] == ESCAPE) {
|
||||
if (ESCAPED.accept.find(f[pos]) != std::string::npos) {
|
||||
discards.push_back(pos++);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Unrecognized escape sequence");
|
||||
}
|
||||
else if (f[pos] == char(NEWLINE)) {
|
||||
else if (f[pos] == NEWLINE) {
|
||||
throw std::runtime_error("Unclosed String");
|
||||
}
|
||||
} while (pos++);
|
||||
|
@ -247,74 +226,110 @@ namespace {
|
|||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// Handles dialogue as well
|
||||
void ParseCommand(ParseState& state, const std::string& f, size_t& pos, Scene& scene) {
|
||||
int initial = pos; // TEMP
|
||||
|
||||
int exp_layer = 0;
|
||||
bool exp_complete = false;
|
||||
|
||||
auto proc = GetToken(f, pos);
|
||||
if (!IsSymbol(proc)) throw std::runtime_error("Illegal Procedure name");
|
||||
|
||||
Command c{ { Type::Symbol, proc }, {} };
|
||||
while (!exp_complete) {
|
||||
SkipComments(f, pos);
|
||||
if (pos > f.find('\n', initial)) // TEMP
|
||||
exp_complete = true; // TEMP
|
||||
auto t = PeekToken(f, pos);
|
||||
if (t[0] == ARRAY_OPEN) {
|
||||
state = ParseState::Array;
|
||||
ParseArray(state, f, pos, c);
|
||||
break;
|
||||
}
|
||||
else if (t[0] == GROUP_OPEN) {
|
||||
|
||||
}
|
||||
else if (t[0] == GROUP_CLOSE) {
|
||||
|
||||
}
|
||||
else if (t[0] == QUOTE) {
|
||||
state = ParseState::String;
|
||||
c.args.push_back(Object{ Type::String, ParseString(state, f, pos) });
|
||||
}
|
||||
else if (t[0] == ARRAY_CLOSE)
|
||||
throw std::runtime_error("Cannot match array");
|
||||
else {
|
||||
auto token = GetToken(f, pos);
|
||||
if (IsNumeric(token)) {
|
||||
c.args.push_back(Object{ Type::Number, std::stof(token) });
|
||||
}
|
||||
else if (IsSymbol(token)) {
|
||||
c.args.push_back(Object{ Type::Symbol, token });
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Illegal symbol");
|
||||
}
|
||||
}
|
||||
|
||||
scene.append(c);
|
||||
unsigned GetProcedureArity(const std::string& key) {
|
||||
return NVL::ENVIRONMENT.get(key).length;
|
||||
}
|
||||
|
||||
void ParseScene(ParseState& state, const std::string& f, size_t& pos, std::vector<Scene>& scenes) {
|
||||
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 } };
|
||||
for (int i = 0; i < GetProcedureArity(proc); i++) {
|
||||
c.push_back(ParseExpression(f, pos));
|
||||
};
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
Object ParseExpression(const std::string& f, size_t& pos) {
|
||||
SkipComments(f, pos);
|
||||
|
||||
auto t = PeekToken(f, pos);
|
||||
if (t[0] == ARRAY_OPEN) {
|
||||
SkipOverFirstChar(f, pos);
|
||||
auto c = ParseArray(f, pos, 0);
|
||||
if (PeekToken(f, pos)[0] != ARRAY_CLOSE)
|
||||
throw std::runtime_error("Cannot match closing Array");
|
||||
else
|
||||
SkipOverFirstChar(f, pos);
|
||||
return c;
|
||||
}
|
||||
else if (t[0] == GROUP_OPEN) {
|
||||
SkipOverFirstChar(f, pos);
|
||||
auto c = ParseCommand(f, pos);
|
||||
if (PeekToken(f, pos)[0] != GROUP_CLOSE)
|
||||
throw std::runtime_error("Cannot match closing subexpression");
|
||||
else
|
||||
SkipOverFirstChar(f, pos);
|
||||
return Object(ParseType::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) };
|
||||
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) };
|
||||
else if (IsLegalSymbolName(token))
|
||||
return { ParseType::Symbol, token };
|
||||
else
|
||||
throw std::runtime_error("Illegal symbol");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Command ParseDialogue(const std::string& s) {
|
||||
return { { ParseType::Symbol, "Say" }, { ParseType::String, s } };
|
||||
}
|
||||
|
||||
Scene ParseScene(const std::string& f, size_t& pos) {
|
||||
SkipComments(f, pos);
|
||||
|
||||
if (!(GetToken(f, pos) == BEGIN.accept))
|
||||
throw std::runtime_error("Could not match accept at root");
|
||||
|
||||
auto scene_name = GetToken(f, pos);
|
||||
if (!IsSymbol(scene_name)) throw std::runtime_error("Illegal Scene name");
|
||||
if (!IsLegalSymbolName(scene_name)) throw std::runtime_error("Illegal Scene name");
|
||||
Scene s{ scene_name };
|
||||
|
||||
state = ParseState::Command;
|
||||
bool dialogue_mode = false;
|
||||
|
||||
while (PeekToken(f, pos) != END.accept) {
|
||||
ParseCommand(state, f, pos, s);
|
||||
if (!dialogue_mode)
|
||||
if (PeekToken(f, pos) == DIALOGUE_OPEN.accept) {
|
||||
dialogue_mode = true;
|
||||
GetToken(f, pos); // skip DIALOGUE_OPEN
|
||||
SkipComments(f, pos);
|
||||
}
|
||||
else
|
||||
s.append(ParseCommand(f, pos));
|
||||
else {
|
||||
auto end = f.find(NEWLINE.accept + DIALOGUE_CLOSE.accept, pos);
|
||||
if (end == std::string::npos)
|
||||
throw std::runtime_error("Dialogue does not terminate");
|
||||
auto lines = split_string_by_lines(f.substr(pos, end - pos));
|
||||
for (auto& l : lines) {
|
||||
s.append(ParseDialogue(l));
|
||||
}
|
||||
dialogue_mode = false;
|
||||
pos = end;
|
||||
GetToken(f, pos); // skip DIALOGUE_CLOSE
|
||||
SkipComments(f, pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (dialogue_mode)
|
||||
throw std::runtime_error("Illegal Scene end");
|
||||
|
||||
GetToken(f, pos); // skip END
|
||||
|
||||
scenes.push_back(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
/*Scene EvalScene(const std::vector<std::vector<Parse>>& parses) { // TODO THIS IS TOTALLY FUCKED
|
||||
|
@ -352,11 +367,10 @@ namespace {
|
|||
namespace NVL {
|
||||
void ParseFile(const std::string& path) {
|
||||
std::string f = read_file_to_string(path);
|
||||
ParseState state = ParseState::Root;
|
||||
|
||||
std::vector<Scene> list {}; // Vector of scenes which each contain a vector of Parses
|
||||
for (size_t i = 0; i < f.length(); i++) {
|
||||
ParseScene(state, f, i, list);
|
||||
list.push_back(ParseScene(f, i));
|
||||
}
|
||||
|
||||
}
|
|
@ -61,6 +61,6 @@ Choice var4 ["Apple", "Orange"]
|
|||
// 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 ("Apple" RouteA) ("Orange" RouteB) (default RouteC))
|
||||
// JUMP (switch var4 [["Apple", RouteA], ["Orange", RouteB], [default, RouteC]])
|
||||
|
||||
END
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
BEGIN Scene1
|
||||
hello "String \\ sdsd \" \\ \" sds"
|
||||
hello "String \\ sdsd \" \\ \" sds" 873482384 .2342342
|
||||
END
|
||||
|
|
Loading…
Add table
Reference in a new issue