Added json parser module

main
anulax1225 ago%!(EXTRA string=11 months)
parent 2ea548678b
commit fc90bfb982
  1. 6
      src/bakatools.h
  2. 144
      src/bakatools/json/json_lexer.cpp
  3. 52
      src/bakatools/json/json_lexer.h
  4. 78
      src/bakatools/json/json_node.cpp
  5. 53
      src/bakatools/json/json_node.h
  6. 264
      src/bakatools/json/json_parser.cpp
  7. 32
      src/bakatools/json/json_parser.h
  8. 3
      src/bakatools/string/format.h
  9. 1
      src/bakatoolspch.h

@ -2,6 +2,7 @@
#ifdef BKMOD_ALL
#define BKMOD_BASE
#define BKMOD_JSON
#define BKMOD_STRING
#define BKMOD_CONTAINER
#define BKMOD_THREAD
@ -11,6 +12,11 @@
#include <bakatools/logging/log.h>
#include <bakatools/logging/assert.h>
#endif
#ifdef BKMOD_JSON
#include <bakatools/json/json_parser.h>
#include <bakatools/json/json_lexer.h>
#include <bakatools/json/json_node.h>
#endif
#ifdef BKMOD_STRING
#include <bakatools/string/string_tools.h>
#endif

@ -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();
};
}

@ -28,9 +28,6 @@ namespace Bk::Tools {
if (!std::is_floating_point<Arg>()) throw std::invalid_argument("%f introduces floating point argument");
break;
case 'b' :
if(arg) ss << "true";
else ss << "false";
return format_impl(ss, ++format, args...);
case 's' :
break;
// etc.

@ -3,6 +3,7 @@
#include <iostream>
#include <cstring>
#include <vector>
#include <map>
#include <string>
#include <cstdint>
#include <cctype>

Loading…
Cancel
Save