/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "benc/serialization/json/JsonBencMessageReader.h"
#include "benc/List.h"
#include "benc/Dict.h"
#include "benc/String.h"
#include "exception/Er.h"
#include "memory/Allocator.h"
#include "wire/Message.h"
#include "util/Gcc.h"
#include "util/Hex.h"
#include "util/Base10.h"
#include
struct Context {
struct Message* const msg;
struct Allocator* const alloc;
const bool lax;
int line;
uintptr_t beginningLastLine;
};
static int getColumn(struct Context* ctx)
{
return (uintptr_t) ctx->msg->msgbytes - ctx->beginningLastLine;
}
#define ERROR0(ctx, message) \
return Er__raise(Gcc_SHORT_FILE, Gcc_LINE, ctx->alloc, \
"Error parsing config (line %d column %d): " message, \
ctx->line, getColumn(ctx))
#define ERROR(ctx, message, ...) \
return Er__raise(Gcc_SHORT_FILE, Gcc_LINE, ctx->alloc, \
"Error parsing config (line %d column %d): " message, \
ctx->line, getColumn(ctx), __VA_ARGS__)
static Er_DEFUN(uint8_t peak(struct Context* ctx))
{
if (!Message_getLength(ctx->msg)) { ERROR0(ctx, "Out of content while reading"); }
Er_ret(ctx->msg->msgbytes[0]);
}
static Er_DEFUN(void skip(struct Context* ctx, int num))
{
if (num > Message_getLength(ctx->msg)) { ERROR0(ctx, "Out of content while reading"); }
for (int i = 0; i < num; i++) {
if (ctx->msg->msgbytes[i] == '\n') {
ctx->beginningLastLine = (uintptr_t) &ctx->msg->msgbytes[i];
ctx->line++;
}
}
Er(Message_eshift(ctx->msg, -num));
Er_ret();
}
static Er_DEFUN(bool assertChar(struct Context* ctx, char chr, bool lax))
{
char c = Er(peak(ctx));
if (c != chr) {
if (lax == true) { Er_ret(false); }
ERROR(ctx, "Expected char [%c] but got [%c]", chr, c);
}
Er_ret(true);
}
static Er_DEFUN(void parseComment(struct Context* ctx))
{
Er(assertChar(ctx, '/', false));
Er(skip(ctx, 1));
uint8_t secondChar = Er(peak(ctx));
if (secondChar != '/' && secondChar != '*') { ERROR(ctx, "Unexpected char [%c]", secondChar); }
bool lastCharSplat = false;
for (;;) {
Er(skip(ctx, 1));
uint8_t chr = Er(peak(ctx));
if (lastCharSplat && secondChar == '*' && chr == '/') {
// get rid of the trailing *
Er(skip(ctx, 1));
} else if (secondChar == '/' && chr == '\n') {
} else {
lastCharSplat = (chr == '*');
continue;
}
Er_ret();
}
}
static Er_DEFUN(void parseWhitespaceAndComments(struct Context* ctx))
{
for (;;) {
switch (Er(peak(ctx))) {
case '\n':
case ' ':
case '\r':
case '\t':
Er(skip(ctx, 1));
continue;
case '/':
Er(parseComment(ctx));
continue;
default: break;
}
Er_ret();
}
ERROR0(ctx, "Reached end of message while parsing");
}
static Er_DEFUN(String* parseString(struct Context* ctx))
{
Er(assertChar(ctx, '"', false));
int line = ctx->line;
uintptr_t beginningLastLine = ctx->beginningLastLine;
int msgLen = Message_getLength(ctx->msg);
String* out = NULL;
uint32_t pos = 0;
#define PUSHOUT(chr) do { \
if (out) { \
Assert_true(out->len > pos); \
out->bytes[pos] = (chr); \
} \
pos++; \
} while (0)
// CHECKFILES_IGNORE expecting a ;
for (;;) {
Er(skip(ctx, 1));
uint8_t bchar = Er(peak(ctx));
switch (bchar) {
case '"': {
Er(skip(ctx, 1));
if (out) { Er_ret(out); }
// got the length, reset and then copy the string next cycle
ctx->line = line;
ctx->beginningLastLine = beginningLastLine;
Er(Message_eshift(ctx->msg, msgLen - Message_getLength(ctx->msg)));
out = String_newBinary(NULL, pos, ctx->alloc);
pos = 0;
continue;
}
case '\0':
case '\n': {
ERROR0(ctx, "Unterminated string");
}
case '\\': {
Er(skip(ctx, 1));
uint8_t x = Er(peak(ctx));
Er(skip(ctx, 1));
if (x != 'x') {
ERROR0(ctx, "Char \\ only allowed if followed by x (as in \\xff)");
}
uint8_t high = Er(peak(ctx));
Er(skip(ctx, 1));
uint8_t low = Er(peak(ctx));
int byte = Hex_decodeByte(high, low);
if (byte < 0 || (byte > 255)) { ERROR0(ctx, "Invalid hex encoding"); }
PUSHOUT(byte);
continue;
}
default: {
PUSHOUT(bchar);
continue;
}
}
}
#undef PUSHOUT
}
static Er_DEFUN(int64_t parseInteger(struct Context* ctx))
{
Er_ret( Er(Base10_read(ctx->msg)) );
}
static Er_DEFUN(Object* parseGeneric(struct Context* ctx));
static Er_DEFUN(List* parseList(struct Context* ctx))
{
Er(assertChar(ctx, '[', false));
Er(skip(ctx, 1));
struct List_Item* first = NULL;
struct List_Item* last = NULL;
for (int i = 0; ; i++) {
for (;;) {
Er(parseWhitespaceAndComments(ctx));
// lax mode, allow ,, extra ,,, commas
if (!ctx->lax || Er(peak(ctx)) != ',') { break; }
Er(skip(ctx, 1));
}
if (Er(peak(ctx)) == ']') {
Er(skip(ctx, 1));
List* out = Allocator_malloc(ctx->alloc, sizeof(List));
*out = first;
Er_ret(out);
}
if (i && Er(assertChar(ctx, ',', ctx->lax))) {
Er(skip(ctx, 1));
Er(parseWhitespaceAndComments(ctx));
}
struct List_Item* item = Allocator_calloc(ctx->alloc, sizeof(struct List_Item), 1);
item->elem = Er(parseGeneric(ctx));
if (last) {
last->next = item;
} else {
first = item;
}
last = item;
}
}
static Er_DEFUN(Dict* parseDict(struct Context* ctx))
{
Er(assertChar(ctx, '{', false));
Er(skip(ctx, 1));
struct Dict_Entry* last = NULL;
struct Dict_Entry* first = NULL;
for (int i = 0; ; i++) {
for (;;) {
Er(parseWhitespaceAndComments(ctx));
if (!ctx->lax || Er(peak(ctx)) != ',') { break; }
Er(skip(ctx, 1));
}
if (Er(peak(ctx)) == '}') {
Er(skip(ctx, 1));
Dict* out = Allocator_malloc(ctx->alloc, sizeof(Dict));
*out = first;
Er_ret(out);
}
if (i && Er(assertChar(ctx, ',', ctx->lax))) {
Er(skip(ctx, 1));
Er(parseWhitespaceAndComments(ctx));
}
struct Dict_Entry* entry = Allocator_calloc(ctx->alloc, sizeof(struct Dict_Entry), 1);
entry->key = Er(parseString(ctx));
Er(parseWhitespaceAndComments(ctx));
if (Er(assertChar(ctx, ':', ctx->lax))) {
Er(skip(ctx, 1));
Er(parseWhitespaceAndComments(ctx));
}
entry->val = Er(parseGeneric(ctx));
if (last) {
last->next = entry;
} else {
first = entry;
}
last = entry;
}
}
static Er_DEFUN(Object* parseGeneric(struct Context* ctx))
{
Object* out = Allocator_calloc(ctx->alloc, sizeof(Object), 1);
uint8_t c = Er(peak(ctx));
switch (c) {
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
out->type = Object_INTEGER;
out->as.number = Er(parseInteger(ctx));
break;
}
case '[': {
out->type = Object_LIST;
out->as.list = Er(parseList(ctx));
break;
}
case '{': {
out->type = Object_DICT;
out->as.dictionary = Er(parseDict(ctx));
break;
}
case '"': {
out->type = Object_STRING;
out->as.string = Er(parseString(ctx));
break;
}
default:
ERROR(ctx, "While looking for something to parse: "
"expected one of - 0 1 2 3 4 5 6 7 8 9 [ { \", found [%c]", c);
}
Er_ret(out);
}
Er_DEFUN(Dict* JsonBencMessageReader_read(
struct Message* msg,
struct Allocator* alloc,
bool lax
)) {
struct Context ctx = {
.msg = msg,
.alloc = alloc,
.lax = lax,
.line = 1,
.beginningLastLine = (uintptr_t) msg->msgbytes
};
Er_ret( Er(parseDict(&ctx)) );
}
const char* JsonBencMessageReader_readNoExcept(
struct Message* msg,
struct Allocator* alloc,
Dict** outPtr,
bool lax)
{
struct Er_Ret* er = NULL;
Dict* out = Er_check(&er, JsonBencMessageReader_read(msg, alloc, lax));
if (er) {
return er->message;
}
*outPtr = out;
return NULL;
}