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