parent
2ea548678b
commit
fc90bfb982
9 changed files with 630 additions and 3 deletions
@ -0,0 +1,144 @@ |
||||
#include "json_lexer.h" |
||||
|
||||
namespace Bk::Json
|
||||
{ |
||||
std::string Token::to_string() |
||||
{ |
||||
return Tools::string_format("%s %d", this->value, (int)this->type); |
||||
} |
||||
|
||||
char Lexer::get_next_char() |
||||
{ |
||||
char c = data.pull<char>(); |
||||
return c; |
||||
} |
||||
|
||||
bool Lexer::has_more_tokens() |
||||
{ |
||||
return (bool)data.size(); |
||||
} |
||||
|
||||
char Lexer::get_without_white_space() |
||||
{ |
||||
char c = ' '; |
||||
while ((c == ' ' || c == '\n')) |
||||
{ |
||||
c = get_next_char(); // check
|
||||
if ((c == ' ' || c == '\n') && !data.size()) |
||||
{ |
||||
throw std::logic_error("Ran out of tokens"); |
||||
} |
||||
else if (!data.size()) |
||||
{ |
||||
return c; |
||||
} |
||||
} |
||||
return c; |
||||
} |
||||
|
||||
Token Lexer::get_token() |
||||
{ |
||||
char c; |
||||
if (!data.size()) |
||||
{ |
||||
throw std::logic_error("Exhausted tokens"); |
||||
} |
||||
c = get_without_white_space(); |
||||
|
||||
Token token; |
||||
if (c == '"') |
||||
{ |
||||
token.type = TokenType::STRING; |
||||
token.value = ""; |
||||
c = get_next_char(); |
||||
while (c != '"') |
||||
{ |
||||
token.value += c; |
||||
c = get_next_char(); |
||||
} |
||||
} |
||||
else if (c == '{') |
||||
{ |
||||
token.type = TokenType::CURLY_OPEN; |
||||
} |
||||
else if (c == '}') |
||||
{ |
||||
token.type = TokenType::CURLY_CLOSE; |
||||
} |
||||
else if (c=='-' || (c >= '0' && c <= '9')) |
||||
{ |
||||
token.type = TokenType::NUMBER; |
||||
token.value = ""; |
||||
token.value += c; |
||||
while ((c=='-')||(c >= '0' && c <= '9') || c == '.') |
||||
{ |
||||
c = get_next_char(); |
||||
if (!data.size()) |
||||
{ |
||||
break; |
||||
} |
||||
else |
||||
{ |
||||
if ((c=='-')||(c >= '0' && c <= '9')||(c=='.')) |
||||
{ |
||||
token.value += c; |
||||
} |
||||
else if (c == ',') |
||||
{ |
||||
roll_back_char(c); |
||||
break; |
||||
} |
||||
else if (c == '\n') break; |
||||
else |
||||
{ |
||||
throw std::logic_error("Bad Numeric format"); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
else if(c=='f'){ |
||||
token.type = TokenType::BOOLEAN; |
||||
token.value = "false"; |
||||
data.pull<char>(4); |
||||
} |
||||
else if(c=='t'){ |
||||
token.type = TokenType::BOOLEAN; |
||||
token.value = "true"; |
||||
data.pull<char>(3); |
||||
} |
||||
else if(c=='n'){ |
||||
token.type = TokenType::NULL_TYPE; |
||||
token.value = "null"; |
||||
data.pull<char>(3); |
||||
} |
||||
else if (c == '[') |
||||
{ |
||||
token.type = TokenType::ARRAY_OPEN; |
||||
} |
||||
else if (c == ']') |
||||
{ |
||||
token.type = TokenType::ARRAY_CLOSE; |
||||
} |
||||
else if (c == ':') |
||||
{ |
||||
token.type = TokenType::COLON; |
||||
} |
||||
else if (c == ',') |
||||
{ |
||||
token.type = TokenType::COMMA; |
||||
} |
||||
lexed_token.push_back(token); |
||||
return token; |
||||
} |
||||
|
||||
Token Lexer::roll_back_token() |
||||
{ |
||||
auto token = lexed_token.back(); |
||||
return token; |
||||
} |
||||
|
||||
void Lexer::roll_back_char(char c) |
||||
{ |
||||
data.push<char>(c); |
||||
} |
||||
} |
@ -0,0 +1,52 @@ |
||||
#pragma once |
||||
|
||||
#include <bakatoolspch.h> |
||||
#include <bakatools/file_system/file.h> |
||||
#include <bakatools/container/data_stream.h> |
||||
#include <bakatools/string/format.h> |
||||
#include <bakatools/logging/log.h> |
||||
#include "json_node.h" |
||||
|
||||
namespace Bk::Json
|
||||
{ |
||||
enum class TokenType |
||||
{ |
||||
CURLY_OPEN, |
||||
CURLY_CLOSE, |
||||
COLON, |
||||
STRING, |
||||
NUMBER, |
||||
ARRAY_OPEN, |
||||
ARRAY_CLOSE, |
||||
COMMA, |
||||
BOOLEAN, |
||||
NULL_TYPE |
||||
}; |
||||
|
||||
struct Token |
||||
{ |
||||
std::string value; |
||||
TokenType type; |
||||
|
||||
std::string to_string(); |
||||
}; |
||||
|
||||
class Lexer |
||||
{ |
||||
std::vector<Token> lexed_token; |
||||
|
||||
public: |
||||
Lexer() {} |
||||
Lexer(Type::DataStream data) |
||||
: data(data) {} |
||||
|
||||
Type::DataStream data; |
||||
|
||||
char get_next_char(); |
||||
char get_without_white_space(); |
||||
Token get_token(); |
||||
bool has_more_tokens(); |
||||
Token roll_back_token(); |
||||
void roll_back_char(char c); |
||||
}; |
||||
} |
@ -0,0 +1,78 @@ |
||||
#include "json_node.h" |
||||
namespace Bk::Json
|
||||
{ |
||||
void Node::set_object(Object *object)
|
||||
{ |
||||
values.object = object; |
||||
type = Type::OBJECT; |
||||
} |
||||
|
||||
Object Node::get_object() |
||||
{ |
||||
if (type == Type::OBJECT)
|
||||
return *values.object; |
||||
throw std::logic_error("Improper return"); |
||||
} |
||||
|
||||
void Node::set_list(List* list) |
||||
{ |
||||
values.list = list; |
||||
type = Type::LIST; |
||||
} |
||||
|
||||
List Node::get_list() |
||||
{ |
||||
if (type == Type::LIST)
|
||||
return *values.list; |
||||
throw std::logic_error("Improper return"); |
||||
} |
||||
|
||||
void Node::set_string(std::string* str) |
||||
{ |
||||
values.s = str; |
||||
type = Type::STRING; |
||||
} |
||||
|
||||
std::string Node::get_string() |
||||
{ |
||||
if (type == Type::STRING)
|
||||
return *values.s; |
||||
throw std::logic_error("Improper return"); |
||||
} |
||||
|
||||
void Node::set_bool(bool value) |
||||
{ |
||||
values.bValue = value; |
||||
type = Type::BOOLEAN; |
||||
} |
||||
|
||||
bool Node::get_bool() |
||||
{ |
||||
if (type == Type::BOOLEAN)
|
||||
return values.bValue; |
||||
throw std::logic_error("Improper return"); |
||||
} |
||||
|
||||
void Node::set_float(float value) |
||||
{ |
||||
values.fValue = value; |
||||
type = Type::NUMBER; |
||||
} |
||||
|
||||
float Node::get_float() |
||||
{ |
||||
if (type == Type::NUMBER)
|
||||
return values.fValue; |
||||
throw std::logic_error("Improper return"); |
||||
} |
||||
|
||||
void Node::set_null() |
||||
{ |
||||
type = Type::NULL_TYPE; |
||||
} |
||||
|
||||
bool Node::is_null() |
||||
{ |
||||
return type == Type::NULL_TYPE; |
||||
} |
||||
} |
@ -0,0 +1,53 @@ |
||||
#pragma once |
||||
|
||||
#include <bakatoolspch.h> |
||||
|
||||
namespace Bk::Json |
||||
{ |
||||
class Node; |
||||
|
||||
using Object = std::map<std::string, std::shared_ptr<Node>>; |
||||
using List = std::vector<std::shared_ptr<Node>>; |
||||
|
||||
class Node
|
||||
{ |
||||
public: |
||||
enum class Type
|
||||
{
|
||||
OBJECT,
|
||||
LIST,
|
||||
STRING,
|
||||
NUMBER,
|
||||
BOOLEAN,
|
||||
NULL_TYPE |
||||
};
|
||||
void set_object(Object* object); |
||||
Object get_object(); |
||||
|
||||
void set_list(List* list); |
||||
List get_list(); |
||||
|
||||
void set_string(std::string* str); |
||||
std::string get_string(); |
||||
|
||||
void set_bool(bool value); |
||||
bool get_bool(); |
||||
|
||||
void set_float(float value); |
||||
float get_float(); |
||||
|
||||
void set_null(); |
||||
bool is_null(); |
||||
|
||||
private: |
||||
union Values { |
||||
Object* object; |
||||
List* list; |
||||
std::string* s; |
||||
float fValue; |
||||
bool bValue; |
||||
}; |
||||
Values values; |
||||
Type type; |
||||
}; |
||||
} |
@ -0,0 +1,264 @@ |
||||
#include "json_parser.h" |
||||
|
||||
namespace Bk::Json |
||||
{ |
||||
Parser::Parser(File& file) |
||||
{ |
||||
Type::DataStream data; |
||||
if (file.exists()) |
||||
{ |
||||
data = file.read(file.size()); |
||||
data.reverse(); |
||||
} |
||||
lexer = Lexer(data); |
||||
} |
||||
|
||||
Parser::Parser(const char* str, int length) |
||||
{ |
||||
Type::DataStream data; |
||||
data.payload = std::vector<char>(str, str + length); |
||||
data.reverse(); |
||||
lexer = Lexer(data); |
||||
} |
||||
|
||||
Parser::Parser(const std::string& str) |
||||
{ |
||||
Type::DataStream data; |
||||
data.payload = std::vector<char>(str.c_str(), str.c_str() + str.length()); |
||||
data.reverse(); |
||||
lexer = Lexer(data); |
||||
} |
||||
|
||||
std::shared_ptr<Json::Node> Parser::parse()
|
||||
{ |
||||
Token token; |
||||
while (lexer.has_more_tokens()) { |
||||
try
|
||||
{ |
||||
token = lexer.get_token(); |
||||
switch (token.type)
|
||||
{ |
||||
case TokenType::CURLY_OPEN:
|
||||
{ |
||||
std::shared_ptr<Json::Node> parsed_object = parse_object(); |
||||
if (!root) { |
||||
root = parsed_object; |
||||
} |
||||
break; |
||||
} |
||||
case TokenType::ARRAY_OPEN: { |
||||
std::shared_ptr<Json::Node> parsed_list = parse_list(); |
||||
if (!root) { |
||||
root = parsed_list; |
||||
} |
||||
break; |
||||
} |
||||
|
||||
case TokenType::STRING: { |
||||
|
||||
std::shared_ptr<Json::Node> parsed_string = parse_string(); |
||||
if (!root) { |
||||
root = parsed_string; |
||||
} |
||||
break; |
||||
} |
||||
case TokenType::NUMBER: { |
||||
std::shared_ptr<Json::Node> parsed_number = parse_number(); |
||||
if (!root) { |
||||
root = parsed_number; |
||||
} |
||||
break; |
||||
} |
||||
|
||||
case TokenType::BOOLEAN: { |
||||
std::shared_ptr<Json::Node> parsed_boolean = parse_boolean(); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
catch(std::logic_error e) |
||||
{ |
||||
BK_INFO(e.what()); |
||||
auto node = std::shared_ptr<Json::Node>(); |
||||
node->set_null(); |
||||
root = node; |
||||
break; |
||||
} |
||||
} |
||||
return root; |
||||
} |
||||
|
||||
std::shared_ptr<Json::Node> Parser::parse_list()
|
||||
{ |
||||
std::shared_ptr<Json::Node> node = std::make_shared<Json::Node>(); |
||||
Json::List *list = new Json::List(); |
||||
bool has_completed = false; |
||||
Token next_token; |
||||
while (!has_completed) { |
||||
if (!lexer.has_more_tokens())
|
||||
{ |
||||
throw std::logic_error("No more tokens"); |
||||
}
|
||||
else
|
||||
{ |
||||
next_token = lexer.get_token(); |
||||
if (next_token.type == TokenType::COLON || next_token.type == TokenType::COMMA) |
||||
continue; |
||||
std::shared_ptr<Json::Node> node; |
||||
switch (next_token.type)
|
||||
{ |
||||
case TokenType::ARRAY_OPEN:
|
||||
{ |
||||
node = parse_list(); |
||||
break; |
||||
} |
||||
case TokenType::CURLY_OPEN:
|
||||
{ |
||||
node = parse_object(); |
||||
break; |
||||
} |
||||
case TokenType::STRING:
|
||||
{ |
||||
node = parse_string(); |
||||
break; |
||||
} |
||||
case TokenType::NUMBER:
|
||||
{ |
||||
node = parse_number(); |
||||
break; |
||||
} |
||||
case TokenType::BOOLEAN:
|
||||
{ |
||||
node = parse_boolean(); |
||||
break; |
||||
} |
||||
case TokenType::NULL_TYPE: { |
||||
node = parse_null(); |
||||
break; |
||||
} |
||||
} |
||||
list->push_back(node); |
||||
if (next_token.type == TokenType::ARRAY_CLOSE) { |
||||
has_completed = true; |
||||
} |
||||
} |
||||
} |
||||
node->set_list(list); |
||||
return node; |
||||
} |
||||
|
||||
std::shared_ptr<Json::Node> Parser::parse_object()
|
||||
{ |
||||
std::string key = ""; |
||||
std::shared_ptr<Json::Node> node = std::make_shared<Json::Node>(); |
||||
Json::Object *key_object_map = new Json::Object(); |
||||
bool has_completed = false; |
||||
bool no_key = true; |
||||
Token next_token; |
||||
while (!has_completed)
|
||||
{ |
||||
if (lexer.has_more_tokens())
|
||||
{ |
||||
if (no_key) |
||||
{ |
||||
next_token = lexer.get_token(); |
||||
if (next_token.type == TokenType::CURLY_CLOSE)
|
||||
{ |
||||
has_completed = true; |
||||
break; |
||||
} |
||||
if (next_token.type == TokenType::COLON || next_token.type == TokenType::COMMA) |
||||
continue; |
||||
key = next_token.value; |
||||
no_key = false; |
||||
} |
||||
next_token = lexer.get_token(); |
||||
if (next_token.type == TokenType::COLON || next_token.type == TokenType::COMMA) |
||||
continue; |
||||
std::shared_ptr<Json::Node> node; |
||||
switch (next_token.type)
|
||||
{ |
||||
case TokenType::STRING:
|
||||
{ |
||||
(*key_object_map)[key] = parse_string(); |
||||
break; |
||||
} |
||||
case TokenType::ARRAY_OPEN:
|
||||
{ |
||||
(*key_object_map)[key] = parse_list(); |
||||
break; |
||||
} |
||||
case TokenType::NUMBER:
|
||||
{ |
||||
(*key_object_map)[key] = parse_number(); |
||||
break; |
||||
} |
||||
case TokenType::CURLY_OPEN:
|
||||
{ |
||||
(*key_object_map)[key] = parse_object(); |
||||
break; |
||||
} |
||||
case TokenType::BOOLEAN:
|
||||
{ |
||||
(*key_object_map)[key] = parse_boolean(); |
||||
break; |
||||
|
||||
} |
||||
case TokenType::NULL_TYPE:
|
||||
{ |
||||
(*key_object_map)[key] = parse_null(); |
||||
break; |
||||
} |
||||
} |
||||
if (next_token.type == TokenType::CURLY_CLOSE)
|
||||
{ |
||||
has_completed = true; |
||||
break; |
||||
} |
||||
no_key = true; |
||||
key = ""; |
||||
} |
||||
else
|
||||
{ |
||||
throw std::logic_error("No more tokens"); |
||||
} |
||||
} |
||||
node->set_object(key_object_map); |
||||
return node;
|
||||
} |
||||
|
||||
std::shared_ptr<Json::Node> Parser::parse_string()
|
||||
{ |
||||
std::shared_ptr<Json::Node> node = std::make_shared<Json::Node>(); |
||||
Token token = lexer.roll_back_token(); |
||||
std::string *sValue = new std::string(token.value); |
||||
node->set_string(sValue); |
||||
return node; |
||||
} |
||||
|
||||
std::shared_ptr<Json::Node> Parser::parse_number()
|
||||
{ |
||||
std::shared_ptr<Json::Node> node = std::make_shared<Json::Node>(); |
||||
Token token = lexer.roll_back_token(); |
||||
float fValue = std::stof(token.value); |
||||
node->set_float(fValue); |
||||
return node; |
||||
} |
||||
|
||||
std::shared_ptr<Json::Node> Parser::parse_boolean()
|
||||
{ |
||||
std::shared_ptr<Json::Node> node = std::make_shared<Json::Node>(); |
||||
Token token = lexer.roll_back_token(); |
||||
bool bValue = token.value == "True" ? true : false; |
||||
node->set_bool(bValue); |
||||
return node; |
||||
} |
||||
|
||||
std::shared_ptr<Json::Node> Parser::parse_null()
|
||||
{ |
||||
std::shared_ptr<Json::Node> node = std::make_shared<Json::Node>(); |
||||
Token token = lexer.roll_back_token(); |
||||
node->set_null(); |
||||
return node; |
||||
} |
||||
} |
@ -0,0 +1,32 @@ |
||||
#pragma once |
||||
|
||||
#include <bakatoolspch.h> |
||||
#include <bakatools/file_system/file.h> |
||||
#include <bakatools/container/data_stream.h> |
||||
#include "json_lexer.h" |
||||
|
||||
namespace Bk::Json |
||||
{ |
||||
class Parser { |
||||
Type::DataStream data; |
||||
std::shared_ptr<Json::Node> root; |
||||
std::unique_ptr<Json::Node> current; |
||||
Lexer lexer; |
||||
|
||||
std::shared_ptr<Json::Node> parse_object(); |
||||
std::shared_ptr<Json::Node> parse_string(); |
||||
std::shared_ptr<Json::Node> parse_number(); |
||||
std::shared_ptr<Json::Node> parse_list(); |
||||
std::shared_ptr<Json::Node> parse_boolean(); |
||||
std::shared_ptr<Json::Node> parse_null(); |
||||
|
||||
public: |
||||
Parser(const Type::DataStream data) |
||||
:lexer(data) {} |
||||
Parser(File& file); |
||||
Parser(const char* data, int length); |
||||
Parser(const std::string& str); |
||||
|
||||
std::shared_ptr<Json::Node> parse(); |
||||
}; |
||||
} |
Loading…
Reference in New Issue