Skip to content

Commit

Permalink
json querying
Browse files Browse the repository at this point in the history
  • Loading branch information
apache-hb committed Apr 1, 2024
1 parent 0f3c4c4 commit 374daba
Show file tree
Hide file tree
Showing 24 changed files with 647 additions and 32 deletions.
7 changes: 5 additions & 2 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Checks:
- 'performance*,portability*,bugprone*,clang-analyzer*,cert*,concurrency*,readability-identifier-naming'
- 'performance*,portability*,bugprone*,clang-analyzer*,cert*,concurrency*,readability*'
- '-bugprone-easily-swappable-parameters'
- '-performance-no-int-to-ptr'
CheckOptions:
Expand All @@ -21,4 +21,7 @@ CheckOptions:
- { key: readability-identifier-naming.GlobalConstantPrefix, value: k }
- { key: readability-identifier-naming.GlobalVariableCase, value: CamelCase }
- { key: readability-identifier-naming.GlobalVariablePrefix, value: g }
HeaderFilterRegex: ''
- { key: readability-identifier-naming.ScopedEnumConstantCase, value: CamelCase }
- { key: readability-identifier-naming.ScopedEnumConstantPrefix, value: '' }
HeaderFileExtensions: ['h']
ImplementationFileExtensions: ['c']
2 changes: 2 additions & 0 deletions src/common/os/include/os/impl/posix.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ enum {

#define CT_OS_INVALID_FILE NULL
#define CT_OS_INVALID_LIBRARY NULL

// TODO: this may actually be UINTPTR_MAX
#define CT_OS_INVALID_MAPPING NULL
#define CT_OS_INVALID_ITER NULL

Expand Down
10 changes: 5 additions & 5 deletions src/common/os/include/os/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ CT_OS_API os_error_t os_file_tell(INOUT_NOTNULL os_file_t *file, OUT_NOTNULL siz
///
/// @return an error if the operation could not be performed
RET_INSPECT
CT_OS_API os_error_t os_file_expand(INOUT_NOTNULL os_file_t *file, size_t size);
CT_OS_API os_error_t os_file_resize(INOUT_NOTNULL os_file_t *file, size_t size);

/// @brief map a file into memory
///
Expand Down Expand Up @@ -332,15 +332,15 @@ CT_OS_API os_error_t os_unmap(INOUT_NOTNULL os_mapping_t *mapping);
///
/// @return the data of the mapping
CT_NODISCARD
CT_OS_API void *os_mapping_data(INOUT_NOTNULL os_mapping_t *mapping);
CT_OS_API void *os_mapping_data(IN_NOTNULL os_mapping_t *mapping);

/// @brief get the size of a file mapping
///
/// @param mapping the mapping to get the size of
///
/// @return the size of the mapping
CT_NODISCARD
CT_OS_API size_t os_mapping_size(INOUT_NOTNULL const os_mapping_t *mapping);
CT_OS_API size_t os_mapping_size(IN_NOTNULL const os_mapping_t *mapping);

/// @brief does the mapping object contain a valid mapping
/// checks if the mapping data exists, not for the validity of the mapping
Expand All @@ -349,15 +349,15 @@ CT_OS_API size_t os_mapping_size(INOUT_NOTNULL const os_mapping_t *mapping);
///
/// @return true if the mapping is valid
CT_NODISCARD
CT_OS_API bool os_mapping_active(INOUT_NOTNULL const os_mapping_t *mapping);
CT_OS_API bool os_mapping_active(IN_NOTNULL const os_mapping_t *mapping);

/// @brief get the name of a file
///
/// @param file the file to get the name of
///
/// @return the name of the file
CT_NODISCARD
CT_OS_API const char *os_file_name(INOUT_NOTNULL const os_file_t *file);
CT_OS_API const char *os_file_name(IN_NOTNULL const os_file_t *file);

/// @}

Expand Down
2 changes: 1 addition & 1 deletion src/common/os/src/posix/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ os_error_t os_file_size(os_file_t *file, size_t *actual)
}

USE_DECL
os_error_t os_file_expand(os_file_t *file, size_t size)
os_error_t os_file_resize(os_file_t *file, size_t size)
{
CTASSERT(file != NULL);

Expand Down
2 changes: 1 addition & 1 deletion src/common/os/src/windows/error.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ size_t os_error_get_string(os_error_t error, char *buffer, size_t size)

// TODO: is there a way of asking for the size of the buffer at all?
// this is what .NET core does, but its not clear if this is correct.
return 0x1000 * sizeof(TCHAR);
return 512 * sizeof(TCHAR);
}

CTASSERT(buffer != NULL);
Expand Down
2 changes: 1 addition & 1 deletion src/common/os/src/windows/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ os_error_t os_file_tell(os_file_t *file, size_t *actual)
}

USE_DECL
os_error_t os_file_expand(os_file_t *file, size_t size)
os_error_t os_file_resize(os_file_t *file, size_t size)
{
CTASSERT(file != NULL);

Expand Down
10 changes: 10 additions & 0 deletions src/support/json/include/json/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ CT_JSON_API json_t *json_map_get(IN_NOTNULL const json_t *json, IN_NOTNULL const
RET_INSPECT
CT_JSON_API json_t *json_array_get(IN_NOTNULL const json_t *json, size_t index);

/// @brief query a json object
///
/// @param json the object to query
/// @param query the query to perform
/// @param arena the arena to use
///
/// @return the result of the query
RET_INSPECT
CT_JSON_API json_t *json_query(IN_NOTNULL json_t *json, IN_NOTNULL const char *query, IN_NOTNULL arena_t *arena);

/// @brief scan an io into a json value
/// scan the contents of an io object into a json value
/// @note if the scan fails, the logger will contain error information
Expand Down
12 changes: 9 additions & 3 deletions src/support/json/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@ json_api = configure_file(
)

json_include = include_directories('.', 'include')
json_impl_include = include_directories('src')
json_impl_include = include_directories('src', 'src/query')
json_args = [ '-DCT_JSON_BUILD=1', generated_args ]

src = [
'src/json.c',
'src/ast.c',
'src/scan.c',
lex.process('src/json.l'),
parse.process('src/json.y')
parse.process('src/json.y'),

'src/query/query.c',
'src/query/query_ast.c',
'src/query/query_scan.c',
lex.process('src/query/query.l'),
parse.process('src/query/query.y'),
]

if has_cpp
Expand All @@ -25,7 +31,7 @@ libjson = library('json', src,
install : not meson.is_subproject(),
c_args : json_args,
cpp_args : json_args,
dependencies : [ core, base, scan, interop, gmp, std, notify, events, util ],
dependencies : [ core, base, scan, interop, gmp, std, notify, events, util, io ],
include_directories : [ json_include, json_impl_include ],
override_options : [ 'unity=off' ]
)
Expand Down
107 changes: 107 additions & 0 deletions src/support/json/src/query/query.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "json/json.h"

#include "arena/arena.h"
#include "interop/compile.h"

#include "io/io.h"
#include "notify/notify.h"
#include "base/panic.h"

#include "query_scan.h"

#include "query_bison.h" // IWYU pragma: keep
#include "query_flex.h" // IWYU pragma: keep
#include "std/typed/vector.h"
#include "std/vector.h"

CT_CALLBACKS(kQueryCallbacks, query);

static json_t *eval_query(json_t *json, const query_ast_t *query, arena_t *arena)
{
if (json == NULL)
return NULL;

switch (query->kind)
{
case eQueryObject:
return json;
case eQueryField:
{
json_t *object = eval_query(json, query->object, arena);
if (!object)
return NULL;

if (object->kind != eJsonObject)
return NULL;

return json_map_get(object, arena_strndup(query->field.text, query->field.length, arena));
}
case eQueryIndex:
{
json_t *object = eval_query(json, query->object, arena);
if (!object)
return NULL;

if (object->kind != eJsonArray)
return NULL;

int idx = mpz_get_si(query->index);
if (idx < 0)
return NULL;

if (idx >= vector_len(object->array))
return NULL;

return vector_get(object->array, idx);
}
case eQueryMap:
{
json_t *object = eval_query(json, query->object, arena);
if (!object)
return NULL;

if (object->kind != eJsonObject)
return NULL;

return json_map_get(object, arena_strndup(query->field.text, query->field.length, arena));
}

default:
CT_NEVER("invalid query kind %d", query->kind);
}
}

USE_DECL
json_t *json_query(json_t *json, const char *query, arena_t *arena)
{
CTASSERT(json != NULL);
CTASSERT(query != NULL);
CTASSERT(arena != NULL);

io_t *io = io_string("query", query, arena);

logger_t *logger = logger_new(arena);

query_scan_t ctx = {
.reports = logger,
};

scan_t *scan = scan_io("json query", io, arena);
scan_set_context(scan, &ctx);

parse_result_t result = scan_buffer(scan, &kQueryCallbacks);

if (result.result != eParseOk)
{
typevec_t *messages = logger_get_events(logger);
for (size_t i = 0; i < typevec_len(messages); i++)
{
event_t *event = typevec_offset(messages, i);
printf("error: %s\n", event->message);
}
return NULL;
}

return eval_query(json, result.tree, arena);
}
42 changes: 42 additions & 0 deletions src/support/json/src/query/query.l
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
%option extra-type="scan_t*"
%option 8bit nodefault
%option noyywrap noinput nounput
%option noyyalloc noyyrealloc noyyfree
%option reentrant bison-bridge bison-locations
%option never-interactive batch
%option prefix="query"

%{
#include "query_bison.h"
#include "interop/flex.h"
#include "interop/memory.h"
#include "cthulhu/events/events.h"
%}

WS [ \t\r\v\n\f]
ESCAPES (\\(['"\?\\abfnrtv]|[0-7]{1,3}|x[a-fA-F0-9]+))

%%

{WS}+ ;

"[" { return LBRACKET; }
"]" { return RBRACKET; }
"." { return DOT; }

[-]"0"[0-7]* { query_parse_integer(yylval->integer, yyextra, *yylloc, yytext, 8); return INTEGER; }
[-]"0"[bB][01]+ { query_parse_integer(yylval->integer, yyextra, *yylloc, yytext + 2, 2); return INTEGER; }
[-]"0"[xX][0-9a-fA-F]+ { query_parse_integer(yylval->integer, yyextra, *yylloc, yytext + 2, 16); return INTEGER; }
[-]?[0-9]+ { query_parse_integer(yylval->integer, yyextra, *yylloc, yytext, 10); return INTEGER; }

[a-zA-Z_][a-zA-Z0-9_]* { query_parse_string(&yylval->string, yyextra, *yylloc, yytext, yyleng); return IDENT; }
\"([^"\\\n]|{ESCAPES})*\" { query_parse_string(&yylval->string, yyextra, *yylloc, yytext + 1, yyleng - 2); return STRING; }

. {
query_scan_t *scan = query_scan_context(yyextra);
evt_scan_unknown(scan->reports, node_new(yyextra, *yylloc), yytext);
}

%%

FLEX_MEMORY(query)
66 changes: 66 additions & 0 deletions src/support/json/src/query/query.y
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
%define parse.error verbose
%define api.pure full
%lex-param { void *scan }
%parse-param { void *scan } { scan_t *x }
%locations
%expect 0
%define api.prefix {query}

%code top {
#include "interop/flex.h"
#include "interop/bison.h"

#include "std/typed/vector.h"
#include "std/vector.h"
}

%code requires {
#include "query_ast.h"
#include "query_scan.h"
#define YYSTYPE QUERYSTYPE
#define YYLTYPE QUERYLTYPE
}

%{
int querylex(void *lval, void *loc, scan_t *scan);
void queryerror(where_t *where, void *state, scan_t *scan, const char *msg);
%}

%union {
mpz_t integer;
text_t string;

query_ast_t *ast;
}

%token
LBRACKET "["
RBRACKET "]"
DOT "."

%token<string>
STRING

%token<integer>
INTEGER

%token<string>
IDENT

%type<ast>
root expr

%start root

%%

root: expr { scan_set(x, $1); }
;

expr: IDENT { $$ = query_ast_object(x, $1); }
| expr DOT IDENT { $$ = query_ast_field(x, $1, $3); }
| expr LBRACKET INTEGER RBRACKET { $$ = query_ast_index(x, $1, $3); }
| expr LBRACKET STRING RBRACKET { $$ = query_ast_map(x, $1, $3); }
;

%%
Loading

0 comments on commit 374daba

Please sign in to comment.