Stream.h
#include <stdbool.h>
#include <stddef.h>
struct Stream
{
const char* data; //utf8 encoded
wchar_t CurrentChar;
int CurrentLine;
int CurrentCol;
int CurrentBytePos;
int NextBytePos;
};
#define STREAM_INIT {0}
wchar_t Stream_Match(struct Stream* stream);
void Stream_Close(struct Stream* stream);
void Stream_Attach(struct Stream* stream, const char* text);
bool Stream_Set(struct Stream* stream, const char* text);
bool Stream_Open(struct Stream* stream, const char* path);
wchar_t Stream_LookAhead(const struct Stream* stream);
wchar_t Stream_LookAhead2(const struct Stream* stream, wchar_t* ch2);
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include "Stream.h"
#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__WINDOWS__)
#define stat _stat
#define strdup _strdup
#endif
void Stream_Attach(struct Stream* stream, const char* text)
{
Stream_Close(stream);
stream->data = text;
}
bool Stream_Set(struct Stream* stream, const char* text)
{
char* data = strdup(text);
if (data)
{
Stream_Attach(stream, data);
}
return data != NULL;
}
bool Stream_Open(struct Stream* stream, const char* path)
{
bool result = false;
struct stat info;
int r = stat(
path,
&info);
if (r == 0)
{
char* data = (char*)malloc(sizeof(char) * info.st_size + 1);
if (data != NULL)
{
FILE* file = fopen(path, "r");
if (file != NULL)
{
//SKIP BOM
if (info.st_size >= 3)
{
fread(data, 1, 3, file);
if (data[0] == (char)0xEF &&
data[1] == (char)0xBB &&
data[2] == (char)0xBF)
{
size_t n = fread(data, 1, info.st_size - 3, file);
data[n] = 0;
}
else
{
size_t n = fread(data + 3, 1, info.st_size - 3, file);
data[3 + n] = 0;
}
}
else
{
size_t n = fread(data, 1, info.st_size, file);
data[n] = 0;
}
fclose(file);
result = true;
Stream_Attach(stream, data);
}
}
}
return result;
}
static wchar_t ReadNextChar(const char* data, int currentPos, int* bytes)
{
//https://www.ietf.org/rfc/rfc3629.txt
//https://www.fileformat.info/info/unicode/utf8.htm
int pos = currentPos;
unsigned u = EOF;
if (data != NULL)
{
int c = data[pos];
if (c == '\0' /*EOF*/)
{
u = EOF;
}
else if ((c & 0x80) == 0)
{
pos++;
u = c;
}
else if ((c & 0xC0) == 0x80)
{
u = EOF;
}
else
{
pos++;
u = (c & 0xE0) == 0xC0 ? (c & 0x1F)
: (c & 0xF0) == 0xE0 ? (c & 0x0F)
: (c & 0xF8) == 0xF0 ? (c & 0x07)
: 0;
if (u == 0)
{
u = EOF;
}
else
{
for (;;)
{
c = data[pos];
pos++;
if (c == EOF)
{
break;
}
else if ((c & 0xC0) == 0x80)
{
u = (u << 6) | (c & 0x3F);
}
else
{
pos--;
break;
}
}
}
}
}
*bytes = pos - currentPos;
return u;
}
wchar_t Stream_LookAhead(const struct Stream* stream)
{
int bytes = 0;
wchar_t ch =
ReadNextChar(stream->data, stream->NextBytePos, &bytes);
return ch;
}
wchar_t Stream_LookAhead2(const struct Stream* stream, wchar_t* ch2)
{
*ch2 = WEOF;
int bytes = 0;
wchar_t ch =
ReadNextChar(stream->data, stream->NextBytePos, &bytes);
if (bytes > 0)
{
*ch2 = ReadNextChar(stream->data, stream->NextBytePos + bytes, &bytes);
}
return ch;
}
wchar_t Stream_Match(struct Stream* stream)
{
//if (stream->CurrentChar)
int bytes = 0;
wchar_t ch =
ReadNextChar(stream->data, stream->NextBytePos, &bytes);
if (bytes > 0)
{
if (stream->CurrentLine == 0)
{
stream->CurrentLine = 1;
}
stream->CurrentBytePos = stream->NextBytePos;
stream->NextBytePos += bytes;
stream->CurrentCol++;
if (ch == '\n') //fopen on windows automatically removes \r
{
stream->CurrentLine++;
stream->CurrentCol = 0;
}
stream->CurrentChar = ch;
}
else if (ch == (wchar_t)EOF)
{
if (stream->CurrentBytePos != stream->NextBytePos)
{
stream->CurrentBytePos = stream->NextBytePos;
stream->CurrentCol++;
stream->CurrentChar = ch;
}
}
return ch;
}
void Stream_Close(struct Stream* stream)
{
free((void*)stream->data);
stream->CurrentCol = 0;
stream->CurrentLine = 0;
stream->NextBytePos = 0;
stream->CurrentBytePos = 0;
stream->CurrentChar = 0;
}
#pragma once
#include "Stream.h"
enum JSTokens
{
TK_JS_NONE,
TK_JS_LEFT_CURLY_BRACKET,
TK_JS_RIGHT_CURLY_BRACKET,
TK_JS_LEFT_SQUARE_BRACKET,
TK_JS_RIGHT_SQUARE_BRACKET,
TK_JS_COMMA,
TK_JS_COLON,
TK_JS_STRING,
TK_JS_NUMBER,
TK_JS_TRUE,
TK_JS_FALSE,
TK_JS_NULL,
TK_JS_EOF,
};
struct JsonScanner
{
struct Stream Stream;
enum JSTokens Token;
int LexemeStart;
int LexemeSize;
};
#define JSONSCANNER_INIT ((struct JsonScanner){STREAM_INIT, TK_JS_NONE, 0, 0})
void JsonScanner_Destroy(struct JsonScanner* scanner);
enum JSTokens JsonScanner_Match(struct JsonScanner* scanner);
void JsonScanner_Destroy(struct JsonScanner* scanner);
bool JsonScanner_Set(struct JsonScanner* scanner, const char* sinkString);
void JsonScanner_Attach(struct JsonScanner* scanner, const char* sinkString);
bool JsonScanner_Open(struct JsonScanner* scanner, const char* path);
#include "JsonScanner.h"
#include <assert.h>
bool JsonScanner_Open(struct JsonScanner* scanner, const char* path)
{
JsonScanner_Destroy(scanner);
*scanner = JSONSCANNER_INIT;
bool b = Stream_Open(&scanner->Stream, path);
if (b)
{
Stream_Match(&scanner->Stream);
}
return b;
}
bool JsonScanner_Set(struct JsonScanner* scanner, const char* json)
{
JsonScanner_Destroy(scanner);
*scanner = JSONSCANNER_INIT;
bool b = Stream_Set(&scanner->Stream, json);
if (b)
{
Stream_Match(&scanner->Stream);
}
return b;
}
void JsonScanner_Attach(struct JsonScanner* scanner, const char* json)
{
JsonScanner_Destroy(scanner);
*scanner = JSONSCANNER_INIT;
Stream_Attach(&scanner->Stream, json);
Stream_Match(&scanner->Stream);
}
void JsonScanner_Destroy(struct JsonScanner* scanner)
{
Stream_Close(&scanner->Stream);
}
static wchar_t MatchChar(struct JsonScanner* scanner)
{
scanner->LexemeSize++;
return Stream_Match(&scanner->Stream);
}
static wchar_t MatchCharWith(struct JsonScanner* scanner, wchar_t wch)
{
wchar_t ch = scanner->Stream.CurrentChar;
if (ch == wch)
{
scanner->LexemeSize++;
Stream_Match(&scanner->Stream);
ch = scanner->Stream.CurrentChar;
}
else
{
//unexpected
}
return ch;
}
enum JSTokens JsonScanner_Match(struct JsonScanner* scanner)
{
wchar_t ch = scanner->Stream.CurrentChar;
while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t')
{
Stream_Match(&scanner->Stream);
ch = scanner->Stream.CurrentChar;
}
scanner->LexemeStart = scanner->Stream.CurrentBytePos;
scanner->LexemeSize = 0;
scanner->Token = TK_JS_NONE;
ch = scanner->Stream.CurrentChar;
if (ch == '{')
{
scanner->Token = TK_JS_LEFT_CURLY_BRACKET;
MatchChar(scanner);
}
else if (ch == '}')
{
scanner->Token = TK_JS_RIGHT_CURLY_BRACKET;
MatchChar(scanner);
}
else if (ch == '[')
{
scanner->Token = TK_JS_LEFT_SQUARE_BRACKET;
MatchChar(scanner);
}
else if (ch == ']')
{
scanner->Token = TK_JS_RIGHT_SQUARE_BRACKET;
MatchChar(scanner);
}
else if (ch == 'f')//false
{
scanner->Token = TK_JS_FALSE;
MatchCharWith(scanner, L'f');
MatchCharWith(scanner, L'a');
MatchCharWith(scanner, L'l');
MatchCharWith(scanner, L's');
MatchCharWith(scanner, L'e');
}
else if (ch == 't')//true
{
scanner->Token = TK_JS_TRUE;
MatchCharWith(scanner, L't');
MatchCharWith(scanner, L'r');
MatchCharWith(scanner, L'u');
MatchCharWith(scanner, L'e');
}
else if (ch == 'n')//null
{
scanner->Token = TK_JS_NULL;
MatchCharWith(scanner, L'n');
MatchCharWith(scanner, L'u');
MatchCharWith(scanner, L'l');
MatchCharWith(scanner, L'l');
}
else if (ch == '"')
{
scanner->Token = TK_JS_STRING;
scanner->LexemeStart++;
Stream_Match(&scanner->Stream);
ch = scanner->Stream.CurrentChar;
for (;;)
{
if (ch == L'\\')
{
//scape
ch = MatchChar(scanner);
switch (ch)
{
case '"':
case '/':
case '\\':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
ch = MatchChar(scanner);
break;
default:
break;
}
}
else if (ch == '"')
{
Stream_Match(&scanner->Stream);
ch = scanner->Stream.CurrentChar;
break;
}
else
{
//qualquer coisa
ch = MatchChar(scanner);
}
}
}
else if (ch == ':')
{
ch = MatchChar(scanner);
scanner->Token = TK_JS_COLON;
}
else if (ch == ',')
{
scanner->Token = TK_JS_COMMA;
ch = MatchChar(scanner);
}
else if (ch == '-' || (ch >= '0' && ch <= '9'))
{
scanner->Token = TK_JS_NUMBER;
ch = MatchChar(scanner);
while (ch >= L'0' && ch <= L'9')
{
ch = MatchChar(scanner);
}
if (ch == L'.')
{
ch = MatchChar(scanner);
while (ch >= L'0' && ch <= L'9')
{
ch = MatchChar(scanner);
}
}
if (ch == L'E' || ch == L'e')
{
ch = MatchChar(scanner);
if (ch == L'+' || ch == L'-')
{
ch = MatchChar(scanner);
}
while (ch >= L'0' && ch <= L'9')
{
ch = MatchChar(scanner);
}
}
}
else if (ch == '\0')
{
scanner->Token = TK_JS_EOF;
}
else
{
//error
}
return scanner->Token;
}
#pragma once
struct Data
{
void (*ExchangeBool)(struct Data* data, const char* name, bool* p);
void (*ExchangeInt)(struct Data* data, const char* name, int* p);
void (*ExchangeText)(struct Data* data, const char* name, char* p, int count);
};
void JsonLoad(const char* file,
void (*Exchange)(struct Data* data, void* p),
void* object);
int LoadCommandLine(int argc,
char* argv[],
void (*Exchange)(struct Data* data, void* p),
void* object);
/*
How to use:
struct X {
int id;
bool bFlag;
char name[10];
};
void ExchangeX(struct Data* data, void* p)
{
struct X* x = (struct X*)p;
data->ExchangeInt(data, "id", &x->id);
data->ExchangeText(data, "name", x->name, 10);
data->ExchangeBool(data, "flag", &x->bFlag);
}
struct X x = {0};
//test.json file is:
//
//{
// "id" : 1,
// "name": "teste",
// "flag" : true
//}
JsonLoad("test.json", ExchangeX, &x);
//In case you have this command line:
//--id 1 --name teste --flag
LoadCommandLine(argc, argv, ExchangeX, &x);
*/
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "JsonExchange.h"
#include "JsonScanner.h"
struct ReadPropertyData
{
struct Data data;
enum JSTokens token;
const char* key;
int keyLen;
const char* value;
int valueLen;
bool bStop;
};
static void ReadPropertyData_ExchangeBool(struct Data* data, const char* name, bool* p)
{
struct ReadPropertyData* pData = (struct ReadPropertyData*)data;
if (!pData->bStop &&
strncmp(name, pData->key, pData->keyLen) == 0)
{
pData->bStop = true;
*p = pData->token == TK_JS_TRUE;
}
}
static void ReadPropertyData_ExchangeInt(struct Data* data, const char* name, int* p)
{
struct ReadPropertyData* pData = (struct ReadPropertyData*)data;
if (!pData->bStop &&
pData->value != NULL &&
strncmp(name, pData->key, pData->keyLen) == 0)
{
pData->bStop = true;
*p = atoi(pData->value);
}
}
static void ReadPropertyData_ExchangeString(struct Data* data, const char* name, char* p, int count)
{
struct ReadPropertyData* pData = (struct ReadPropertyData*)data;
if (!pData->bStop &&
pData->value != NULL &&
strncmp(name, pData->key, pData->keyLen) == 0)
{
pData->bStop = true;
int n = pData->valueLen < count - 1 ? pData->valueLen : count - 1;
strncpy(p, pData->value, n);
p[n] = 0;
}
}
void JsonLoad(const char* file,
void (*Exchange)(struct Data* data, void* p),
void* object)
{
struct ReadPropertyData data = { 0 };
data.data.ExchangeBool = ReadPropertyData_ExchangeBool;
data.data.ExchangeInt = ReadPropertyData_ExchangeInt;
data.data.ExchangeText = ReadPropertyData_ExchangeString;
struct JsonScanner scanner = JSONSCANNER_INIT;
if (JsonScanner_Open(&scanner, file))
{
JsonScanner_Match(&scanner); //TK_NONE
enum JSTokens tk = JsonScanner_Match(&scanner); //{
for (;;)
{
data.key = &scanner.Stream.data[scanner.LexemeStart];
data.keyLen = scanner.LexemeSize;
tk = JsonScanner_Match(&scanner); //name
tk = JsonScanner_Match(&scanner); //:
data.value = &scanner.Stream.data[scanner.LexemeStart];
data.valueLen = scanner.LexemeSize;
data.token = tk;
tk = JsonScanner_Match(&scanner); //value
data.bStop = false;
Exchange(&data.data, object);
if (tk != TK_JS_COMMA)
break;
tk = JsonScanner_Match(&scanner); //value
}
}
}
int LoadCommandLine(int argc,
char* argv[],
void (*Exchange)(struct Data* data, void* p),
void* object)
{
struct ReadPropertyData data = { 0 };
data.data.ExchangeBool = ReadPropertyData_ExchangeBool;
data.data.ExchangeInt = ReadPropertyData_ExchangeInt;
data.data.ExchangeText = ReadPropertyData_ExchangeString;
for (int i = 1; i < argc;)
{
data.bStop = false;
if (argv[i][0] == '-' && argv[i][1] == '-')
{
data.key = &argv[i][2];
data.keyLen = strlen(&argv[i][2]);
//Este null evita que seja tentado em propriedades
//nao boleanas
data.value = NULL;
data.valueLen = 0;
data.token = TK_JS_TRUE;
//Tenta como se fosse um flag
Exchange(&data.data, object);
if (data.bStop)
{
//conseguiu como um flag
i++;
}
else
{
//nao era flag le mais um para pegar valor
i++;
if (i < argv)
{
data.value = argv[i];
data.valueLen = strlen(argv[i]);
data.token = TK_JS_NONE;
Exchange(&data.data, object);
i++;
}
}
}
else
{
i++;
}
}
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "JsonExchange.h"
struct X
{
int id;
bool bFlag;
char name[10];
};
void ExchangeX(struct Data* data, void* p)
{
struct X* x = (struct X*)p;
data->ExchangeInt(data, "id", &x->id);
data->ExchangeText(data, "name", x->name, 10);
data->ExchangeBool(data, "flag", &x->bFlag);
}
void Test3()
{
struct X x = {0};
JsonLoad("test.json", ExchangeX, &x);
}
int main(int argc, char* argv[])
{
//struct User user = { 0 };
//ParseJsonObject("user.json", &User, &user);
//Test2();
Test3();
struct X x = {0};
LoadCommandLine(argc, argv, ExchangeX, &x);
}