From fda36378712fc7b2615f433cd670bc6300a3b0e1 Mon Sep 17 00:00:00 2001 From: rl-d Date: Tue, 7 Sep 2021 22:03:07 -0400 Subject: [PATCH] GraphQL lexer and parser (without optional type system) --- ccan/graphql/LICENSE | 1 + ccan/graphql/_info | 68 + ccan/graphql/graphql.c | 1028 +++++++++++++ ccan/graphql/graphql.h | 229 +++ ccan/graphql/test/run.c | 3177 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 4503 insertions(+) create mode 120000 ccan/graphql/LICENSE create mode 100644 ccan/graphql/_info create mode 100644 ccan/graphql/graphql.c create mode 100644 ccan/graphql/graphql.h create mode 100644 ccan/graphql/test/run.c diff --git a/ccan/graphql/LICENSE b/ccan/graphql/LICENSE new file mode 120000 index 00000000..2354d129 --- /dev/null +++ b/ccan/graphql/LICENSE @@ -0,0 +1 @@ +../../licenses/BSD-MIT \ No newline at end of file diff --git a/ccan/graphql/_info b/ccan/graphql/_info new file mode 100644 index 00000000..efdb6a13 --- /dev/null +++ b/ccan/graphql/_info @@ -0,0 +1,68 @@ +#include "config.h" +#include +#include + +/** + * graphql - Routines to lex and parse GraphQL. + * + * This code contains routines to lex and parse GraphQL code. + * This code was written per the spec at: + * https://spec.graphql.org/draft/ + * ...dated Fri, May 21, 2021 at the time of writing. + * Copyright (c) 2021 WhiteCloudFarm.org + * + * + * Example: + * #include + * #include "ccan/graphql/graphql.h" + * + * int main(int argc, char *argv[]) + * + * const char *input_string = "{ fieldName }"; + * struct list_head *output_tokens; + * struct graphql_executable_document *output_document; + * + * const char *errmsg = graphql_lexparse( + * input_string, + * NULL, // tal context + * &output_tokens, // variable to receive tokens + * &output_document); // variable to receive AST + * + * if (errmsg) { + * struct graphql_token *last_token; + * last_token = list_tail(output_tokens, struct graphql_token, list); + * printf("Line %d, col %d: %s", + * last_token->source_line, + * last_token->source_column + last_token->source_len, + * errmsg); + * } else { + * // Normally you would check every indirection in the resulting AST for null + * // pointers, but for simplicity of example: + * printf("A field from the parsed string: %s\n", + * output_document->first_def->op_def->sel_set-> + * first->field->name->token_string); + * } + * + * output_tokens = tal_free(output_tokens); + * } + * + * License: BSD-MIT + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/list\n"); + printf("ccan/take\n"); + printf("ccan/tal\n"); + printf("ccan/tal/str\n"); + printf("ccan/utf8\n"); + return 0; + } + + return 1; +} + diff --git a/ccan/graphql/graphql.c b/ccan/graphql/graphql.c new file mode 100644 index 00000000..e2b70ef0 --- /dev/null +++ b/ccan/graphql/graphql.c @@ -0,0 +1,1028 @@ + +#include "graphql.h" + +#include "ccan/tal/str/str.h" +#include "ccan/utf8/utf8.h" + + +// GraphQL character classes + +#define SOURCE_CHAR(c) ((c) == 9 || (c) == 10 || (c) == 13 || ((c) >= 32 && (c) <= 65535)) +#define WHITE_SPACE(c) ((c) == 9 || (c) == 32) +#define LINE_TERMINATOR(c) ((c) == 10 || (c) == 13) +#define COMMENT(c) ((c) == 35) +#define COMMENT_CHAR(c) (SOURCE_CHAR(c) && !LINE_TERMINATOR(c)) +#define STRING_CHAR(c) (SOURCE_CHAR(c) && !LINE_TERMINATOR(c) && (c)!='"' && (c)!='\\') +#define BLOCK_STRING_CHAR(c) (SOURCE_CHAR(c)) +#define COMMA(c) ((c) == 44) +#define EOF_CHAR(c) ((c) == 0 || (c) == 4) +#define PUNCTUATOR(c) (strchr("!$&().:=@[]{|}", c)) +#define HEX_DIGIT(c) (DIGIT(c) || ((c) >= 0x61 && (c) <= 0x66) || ((c) >= 0x41 && (c) <= 0x46)) +#define DIGIT(c) ((c) >= 0x30 && (c) <= 0x39) +#define NAME_START(c) (((c) >= 0x61 && (c) <= 0x7A) || ((c) >= 0x41 && (c) <= 0x5A) || (c) == 0x5F) +#define NAME_CONTINUE(c) (NAME_START(c) || DIGIT(c)) + + +// Parser shorthands + +#define RET void * +#define PARAMS struct list_head *tokens, struct list_head *used, const char **err +#define ARGS tokens, used, err +#define INIT(type) \ + struct graphql_token *rollback_top = list_top(tokens, struct graphql_token, list); \ + struct graphql_##type *obj = tal(tokens, struct graphql_##type); memset(obj, 0, sizeof(struct graphql_##type)); \ + +#define EXIT \ + exit_label: \ + if (*err) obj = tal_free(obj); \ + return obj; \ + +#define CONSUME_ONE { list_add(used, list_pop(tokens, struct graphql_token, list)); } +#define RESTORE_ONE { list_add(tokens, list_pop(used, struct graphql_token, list)); } +#define ROLLBACK(args) { while (list_top(tokens, struct graphql_token, list) != rollback_top) { RESTORE_ONE; } } +#define OR if (!*err) goto exit_label; *err = NULL; +#define REQ if (*err) { ROLLBACK(args); goto exit_label; } +#define OPT *err = NULL; +#define WHILE_OPT while(!*err); *err = NULL; +#define LOOKAHEAD(args, tok) struct graphql_token *tok = list_top(tokens, struct graphql_token, list); +#define MSG(msg) if (*err) *err = msg; + + +// Parser functions + +RET parse_document(PARAMS); +RET parse_definition(PARAMS); +RET parse_executable_document(PARAMS); +RET parse_executable_definition(PARAMS); +RET parse_operation_definition(PARAMS); +RET parse_operation_type(PARAMS); +RET parse_selection_set(PARAMS); +RET parse_selection(PARAMS); +RET parse_field(PARAMS); +RET parse_arguments(PARAMS); +RET parse_argument(PARAMS); +RET parse_alias(PARAMS); +RET parse_fragment_spread(PARAMS); +RET parse_fragment_definition(PARAMS); +RET parse_fragment_name(PARAMS); +RET parse_type_condition(PARAMS); +RET parse_inline_fragment(PARAMS); +RET parse_value(PARAMS); +RET parse_int_value(PARAMS); +RET parse_negative_sign(PARAMS); +RET parse_non_zero_digit(PARAMS); +RET parse_float_value(PARAMS); +RET parse_boolean_value(PARAMS); +RET parse_string_value(PARAMS); +RET parse_string_character(PARAMS); +RET parse_escaped_unicode(PARAMS); +RET parse_escaped_character(PARAMS); +RET parse_block_string_character(PARAMS); +RET parse_null_value(PARAMS); +RET parse_enum_value(PARAMS); +RET parse_list_value(PARAMS); +RET parse_object_value(PARAMS); +RET parse_object_field(PARAMS); +RET parse_variable(PARAMS); +RET parse_variable_definitions(PARAMS); +RET parse_variable_definition(PARAMS); +RET parse_default_value(PARAMS); +RET parse_type(PARAMS); +RET parse_named_type(PARAMS); +RET parse_list_type(PARAMS); +RET parse_non_null_type(PARAMS); +RET parse_non_null_type_1(PARAMS); +RET parse_non_null_type_2(PARAMS); +RET parse_directives(PARAMS); +RET parse_directive(PARAMS); +RET parse_type_system_document(PARAMS); +RET parse_type_system_definition(PARAMS); +RET parse_type_system_extension_document(PARAMS); +RET parse_type_system_definition_or_extension(PARAMS); +RET parse_type_system_extension(PARAMS); +RET parse_description(PARAMS); +RET parse_schema_definition(PARAMS); +RET parse_root_operation_type_definition(PARAMS); +RET parse_schema_extension(PARAMS); +RET parse_type_definition(PARAMS); +RET parse_type_extension(PARAMS); +RET parse_scalar_type_definition(PARAMS); +RET parse_scalar_type_extension(PARAMS); +RET parse_object_type_definition(PARAMS); +RET parse_implements_interfaces(PARAMS); +RET parse_fields_definition(PARAMS); +RET parse_field_definition(PARAMS); +RET parse_arguments_definition(PARAMS); +RET parse_input_value_definition(PARAMS); +RET parse_object_type_extension(PARAMS); +RET parse_interface_type_definition(PARAMS); +RET parse_interface_type_extension(PARAMS); +RET parse_union_type_definition(PARAMS); +RET parse_union_member_types(PARAMS); +RET parse_union_type_extension(PARAMS); +RET parse_enum_type_definition(PARAMS); +RET parse_enum_values_definition(PARAMS); +RET parse_enum_value_definition(PARAMS); +RET parse_enum_type_extension(PARAMS); +RET parse_input_object_type_definition(PARAMS); +RET parse_input_fields_definition(PARAMS); +RET parse_directive_definition(PARAMS); +RET parse_directive_locations(PARAMS); +RET parse_directive_location(PARAMS); +RET parse_executable_directive_location(PARAMS); +RET parse_type_system_directive_location(PARAMS); + +RET parse_keyword(PARAMS, const char *keyword, const char *errmsg); +RET parse_punct(PARAMS, int punct); +RET parse_name(PARAMS); +RET parse_int(PARAMS); +RET parse_float(PARAMS); +RET parse_string(PARAMS); + +// Convert input string into AST. +const char *graphql_lexparse(const char *input, const tal_t *ctx, struct list_head **tokens, struct graphql_executable_document **doc) { + const char *err = graphql_lex(input, ctx, tokens); + if (!err) + err = graphql_parse(*tokens, doc); + return err; +} + +// Convert lexed tokens into AST. +const char *graphql_parse(struct list_head *tokens, struct graphql_executable_document **doc) { + struct list_head used = LIST_HEAD_INIT(used); + const char *err = NULL; + *doc = parse_executable_document(tokens, &used, &err); + if (err) + return err; +} + +/* The following parser functions follow special rules: + * - The declaration is standardized with RET and PARAMS + * - The "err" argument is assumed to be NULL upon entrance + * - The "err" argument is set on failure + * - If the function fails to parse, then "tokens" shall be as it was upon entrance + * - INIT and EXIT macros are used + * - Macros such as REQ and OPT facilitate readability and conciseness + */ + +RET parse_document(PARAMS) { + INIT(document); + obj->first_def = parse_definition(ARGS); REQ + struct graphql_definition *p = obj->first_def; + do { + p->next_def = parse_definition(ARGS); + p = p->next_def; + } WHILE_OPT; + EXIT; +} + +RET parse_definition(PARAMS) { + INIT(definition); + obj->executable_def = parse_executable_definition(ARGS); +/* OR + obj->type_system_def = parse_type_system_definition_or_extension(ARGS); + // NOTE: Optional type system is not (yet) implemented. +*/ + EXIT; +} + +RET parse_executable_document(PARAMS) { + INIT(executable_document); + obj->first_def = parse_executable_definition(ARGS); REQ + struct graphql_executable_definition *p = obj->first_def; + do { + p->next_def = parse_executable_definition(ARGS); + p = p->next_def; + } WHILE_OPT; + EXIT; +} + +RET parse_executable_definition(PARAMS) { + INIT(executable_definition); + obj->op_def = parse_operation_definition(ARGS); MSG("invalid operation or fragment definition"); OR + obj->frag_def = parse_fragment_definition(ARGS); MSG("invalid operation or fragment definition"); + EXIT; +} + +RET parse_operation_definition(PARAMS) { + INIT(operation_definition); + obj->op_type = parse_operation_type(ARGS); + if (!*err) { + obj->op_name = parse_name(ARGS); OPT + obj->vars = parse_variable_definitions(ARGS); OPT + obj->directives = parse_directives(ARGS); OPT + } else + *err = NULL; + obj->sel_set = parse_selection_set(ARGS); + if (*err) ROLLBACK(ARGS); + EXIT; +} + +RET parse_operation_type(PARAMS) { + INIT(operation_type); + const char *errmsg = "expected: query, mutation, or subscription"; + obj->op_type = parse_keyword(ARGS, "query", errmsg); OR + obj->op_type = parse_keyword(ARGS, "mutation", errmsg); OR + obj->op_type = parse_keyword(ARGS, "subscription", errmsg); + EXIT; +} + +RET parse_selection_set(PARAMS) { + INIT(selection_set); + parse_punct(ARGS, '{'); REQ; + obj->first = parse_selection(ARGS); REQ; + struct graphql_selection *p = obj->first; + parse_punct(ARGS, '}'); + while (*err) { + *err = NULL; + p->next = parse_selection(ARGS); MSG("expected: selection or '}'"); REQ; + p = p->next; + parse_punct(ARGS, '}'); + } + EXIT; +} + +RET parse_selection(PARAMS) { + INIT(selection); + obj->field = parse_field(ARGS); OR + obj->frag_spread = parse_fragment_spread(ARGS); OR + obj->inline_frag = parse_inline_fragment(ARGS); + MSG("expected: field, fragment spread, or inline fragment"); + EXIT; +} + +RET parse_field(PARAMS) { + INIT(field); + obj->alias = parse_alias(ARGS); OPT + obj->name = parse_name(ARGS); REQ + obj->args = parse_arguments(ARGS); OPT + obj->directives = parse_directives(ARGS); OPT + obj->sel_set = parse_selection_set(ARGS); OPT + EXIT; +} + +RET parse_arguments(PARAMS) { + INIT(arguments); + parse_punct(ARGS, '('); REQ + obj->first = parse_argument(ARGS); REQ + struct graphql_argument *p = obj->first; + parse_punct(ARGS, ')'); + while (*err) { + *err = NULL; + p->next = parse_argument(ARGS); MSG("expected: argument or ')'"); REQ; + p = p->next; + parse_punct(ARGS, ')'); + } + EXIT; +} + +RET parse_argument(PARAMS) { + INIT(argument); + obj->name = parse_name(ARGS); REQ + parse_punct(ARGS, ':'); REQ + obj->val = parse_value(ARGS); REQ + EXIT; +} + +RET parse_alias(PARAMS) { + INIT(alias); + obj->name = parse_name(ARGS); REQ + parse_punct(ARGS, ':'); REQ + EXIT; +} + +RET parse_fragment_spread(PARAMS) { + INIT(fragment_spread); + parse_punct(ARGS, 0x2026); REQ // ... + obj->name = parse_fragment_name(ARGS); REQ + obj->directives = parse_directives(ARGS); OPT + EXIT; +} + +RET parse_fragment_definition(PARAMS) { + INIT(fragment_definition); + parse_keyword(ARGS, "fragment", "fragment expected"); REQ + obj->name = parse_fragment_name(ARGS); REQ + obj->type_cond = parse_type_condition(ARGS); REQ + obj->directives = parse_directives(ARGS); OPT + obj->sel_set = parse_selection_set(ARGS); REQ + EXIT; +} + +RET parse_fragment_name(PARAMS) { + INIT(fragment_name); + obj->name = parse_name(ARGS); REQ + struct graphql_token *tok = list_top(used, struct graphql_token, list); + if (streq(tok->token_string, "on")) { + *err = "invalid fragment name"; + ROLLBACK(ARGS); + } + EXIT; +} + +RET parse_type_condition(PARAMS) { + INIT(type_condition); + parse_keyword(ARGS, "on", "expected: 'on'"); REQ + obj->named_type = parse_named_type(ARGS); REQ + EXIT; +} + +RET parse_inline_fragment(PARAMS) { + INIT(inline_fragment); + parse_punct(ARGS, 0x2026); REQ // ... + obj->type_cond = parse_type_condition(ARGS); OPT + obj->directives = parse_directives(ARGS); OPT + obj->sel_set = parse_selection_set(ARGS); REQ + EXIT; +} + +RET parse_value(PARAMS) { + INIT(value); + obj->var = parse_variable(ARGS); // FIXME: if not const + OR + obj->int_val = parse_int_value(ARGS); OR + obj->float_val = parse_float_value(ARGS); OR + obj->str_val = parse_string_value(ARGS); OR + obj->bool_val = parse_boolean_value(ARGS); OR + obj->null_val = parse_null_value(ARGS); OR + obj->enum_val = parse_enum_value(ARGS); OR + obj->list_val = parse_list_value(ARGS); OR + obj->obj_val = parse_object_value(ARGS); + EXIT; +} + +RET parse_int_value(PARAMS) { + INIT(int_value); + obj->val = parse_int(ARGS); + EXIT; +} + +RET parse_float_value(PARAMS) { + INIT(float_value); + obj->val = parse_float(ARGS); + EXIT; +} + +RET parse_boolean_value(PARAMS) { + INIT(boolean_value); + obj->val = parse_keyword(ARGS, "true", "invalid boolean value"); OR + obj->val = parse_keyword(ARGS, "false", "invalid boolean value"); + EXIT; +} + +RET parse_string_value(PARAMS) { + INIT(string_value); + obj->val = parse_string(ARGS); + EXIT; +} + +RET parse_null_value(PARAMS) { + INIT(null_value); + obj->val = parse_keyword(ARGS, "null", "null expected"); + EXIT; +} + +RET parse_enum_value(PARAMS) { + INIT(enum_value); + obj->val = parse_name(ARGS); REQ + struct graphql_token *tok = list_top(used, struct graphql_token, list); + if (streq(tok->token_string, "true") + || streq(tok->token_string, "false") + || streq(tok->token_string, "null")) { + *err = "enum value cannot be true, false, or null"; + ROLLBACK(ARGS); + } + EXIT; +} + +RET parse_list_value(PARAMS) { + INIT(list_value); + parse_punct(ARGS, '['); REQ + parse_punct(ARGS, ']'); + while (*err) { + *err = NULL; + parse_value(ARGS); MSG("expected: value or ']'"); REQ + parse_punct(ARGS, ']'); + } + EXIT; +} + +RET parse_object_value(PARAMS) { + INIT(object_value); + parse_punct(ARGS, '{'); REQ + parse_punct(ARGS, '}'); + struct graphql_object_field *p = NULL; + while (*err) { + *err = NULL; + if (!p) { + obj->first = p = parse_object_field(ARGS); MSG("expected: object field or '}'"); REQ + } else { + p->next = parse_object_field(ARGS); MSG("expected: object field or '}'"); REQ + p = p->next; + } + parse_punct(ARGS, '}'); + } + EXIT; +} + +RET parse_object_field(PARAMS) { + INIT(object_field); + obj->name = parse_name(ARGS); REQ + parse_punct(ARGS, ':'); REQ + obj->val = parse_value(ARGS); REQ + EXIT; +} + +RET parse_variable(PARAMS) { + INIT(variable); + parse_punct(ARGS, '$'); REQ + obj->name = parse_name(ARGS); REQ + EXIT; +} + +RET parse_variable_definitions(PARAMS) { + INIT(variable_definitions); + parse_punct(ARGS, '('); REQ + obj->first = parse_variable_definition(ARGS); REQ + struct graphql_variable_definition *p = obj->first; + parse_punct(ARGS, ')'); + while (*err) { + *err = NULL; + p->next = parse_variable_definition(ARGS); MSG("expected: variable definition or ')'"); REQ + p = p->next; + parse_punct(ARGS, ')'); + } + EXIT; +} + +RET parse_variable_definition(PARAMS) { + INIT(variable_definition); + obj->var = parse_variable(ARGS); REQ + parse_punct(ARGS, ':'); REQ + obj->type = parse_type(ARGS); REQ + obj->default_val = parse_default_value(ARGS); OPT + obj->directives = parse_directives(ARGS); OPT + EXIT; +} + +RET parse_default_value(PARAMS) { + INIT(default_value); + parse_punct(ARGS, '='); REQ + obj->val = parse_value(ARGS); REQ + EXIT; +} + +RET parse_type(PARAMS) { + INIT(type); + obj->named = parse_named_type(ARGS); +/* + OR + obj->list = parse_list_type(ARGS); OR + obj->non_null = parse_non_null_type(ARGS); +*/ + EXIT; +} + +RET parse_named_type(PARAMS) { + INIT(named_type); + obj->name = parse_name(ARGS); + EXIT; +} + +/* +RET parse_list_type(PARAMS) { + INIT(list_type); + parse_punct(ARGS, '['); REQ + parse_type(ARGS); REQ + parse_punct(ARGS, ']'); REQ + EXIT; +} + +RET parse_non_null_type(PARAMS) { + INIT(non_null_type); + parse_non_null_type_1(ARGS); OR + parse_non_null_type_2(ARGS); + EXIT; +} + +RET parse_non_null_type_1(PARAMS) { + INIT(non_null_type); + parse_named_type(ARGS); REQ; + parse_punct(ARGS, '!'); REQ; + EXIT; +} + +RET parse_non_null_type_2(PARAMS) { + INIT(non_null_type); + parse_list_type(ARGS); REQ; + parse_punct(ARGS, '!'); REQ; + EXIT; +} +*/ + +RET parse_directives(PARAMS) { + INIT(directives); + obj->first = parse_directive(ARGS); REQ + struct graphql_directive *p = obj->first; + do { + p->next = parse_directive(ARGS); + p = p->next; + } WHILE_OPT; + EXIT; +} + +RET parse_directive(PARAMS) { + INIT(directive); + parse_punct(ARGS, '@'); REQ + obj->name = parse_name(ARGS); REQ + obj->args = parse_arguments(ARGS); OPT + EXIT; +} + + +/* The following functions construct the "leaves" of the abstract syntax tree. */ + +RET parse_keyword(PARAMS, const char *keyword, const char *errmsg) { + struct graphql_token *tok = list_top(tokens, struct graphql_token, list); + if (!tok || tok->token_type != 'a') { + *err = errmsg; return; + } + if (!streq(tok->token_string, keyword)) { + *err = errmsg; return; + } + CONSUME_ONE(ARGS); + return tok; +} + +// Note: a static buffer is used here. +RET parse_punct(PARAMS, int punct) { + static char punctbuf[16]; + struct graphql_token *tok = list_top(tokens, struct graphql_token, list); + if (!tok || tok->token_type != punct) { + if (punct == 0x2026) + sprintf(punctbuf, "expected: '...'"); + else + sprintf(punctbuf, "expected: '%c'", punct); + *err = punctbuf; return; + } + CONSUME_ONE(ARGS); + return tok; +} + +RET parse_name(PARAMS) { + struct graphql_token *tok = list_top(tokens, struct graphql_token, list); + if (!tok || tok->token_type != 'a') { + *err = "name expected"; return 0; + } + CONSUME_ONE(ARGS); + return tok; +} + +RET parse_int(PARAMS) { + struct graphql_token *tok = list_top(tokens, struct graphql_token, list); + if (!tok || tok->token_type != 'i') { + *err = "integer expected"; return; + } + CONSUME_ONE(ARGS); + return tok; +} + +RET parse_float(PARAMS) { + struct graphql_token *tok = list_top(tokens, struct graphql_token, list); + if (!tok || tok->token_type != 'f') { + *err = "float expected"; return; + } + CONSUME_ONE(ARGS); + return tok; +} + +RET parse_string(PARAMS) { + struct graphql_token *tok = list_top(tokens, struct graphql_token, list); + if (!tok || tok->token_type != 's') { + *err = "string expected"; return; + } + CONSUME_ONE(ARGS); + return tok; +} + + +// Convert input string into tokens. +const char *graphql_lex(const char *input, const tal_t *ctx, struct list_head **tokens) { + + unsigned int c; + const char *p, *line_beginning; + unsigned int line_num = 1; + struct list_head *tok_list; + struct graphql_token *tok; + + // Initialize token output list. + tok_list = tal(ctx, struct list_head); + if (tokens) + *tokens = tok_list; + list_head_init(tok_list); + + // Note: label and goto are used here like a continue statement except that + // it skips iteration, for when characters are fetched in the loop body. + p = input; + line_beginning = p; + do { + c = *p++; +newchar: + // Consume line terminators and increment line counter. + if (LINE_TERMINATOR(c)) { + unsigned int c0 = c; + c = *p++; + if (c0 == 10 || c0 == 13) + line_num++; + if (c0 == 13 && c == 10) + c = *p++; + line_beginning = p - 1; + goto newchar; + } + + // Consume other ignored tokens. + if (COMMA(c) || WHITE_SPACE(c)) { + c = *p++; + goto newchar; + } + if (COMMENT(c)) { + while ((c = *p++) != EOF && !EOF_CHAR(c) && COMMENT_CHAR(c)) + ; // No-op + goto newchar; + } + + // Return success when end is reached. + if (EOF_CHAR(c)) + return GRAPHQL_SUCCESS; + + // Punctuator tokens. + if (PUNCTUATOR(c)) { + + // Note beginning of token in input. + const char *start = p - 1; + + // Handle the ... multi-character case. + if (c == '.') { + c = *p++; + if (c != '.') + return "unrecognized punctuator"; + c = *p++; + if (c != '.') + return "unrecognized punctuator"; + c = 0x2026; + } + + tok = tal(tok_list, struct graphql_token); + list_add_tail(tok_list, tok); + tok->token_type = c; + tok->token_specific = c; + tok->token_string = NULL; + tok->source_line = line_num; + tok->source_column = start - line_beginning + 1; + tok->source_len = p - start; + + } else if (NAME_START(c)) { + + // Name/identifier tokens. + tok = tal(tok_list, struct graphql_token); + list_add_tail(tok_list, tok); + tok->token_type = 'a'; + tok->token_specific = 'a'; + // tok->token_string updated below. + tok->source_line = line_num; + tok->source_column = p - line_beginning; + // tok->source_len updated below. + + // Note the beginning of the name. + const char *name_begin = p - 1; + const char *name_end; + int name_len; + + // Consume the rest of the token. + do { + c = *p++; + } while (NAME_CONTINUE(c)); + + // Note the end of the name and calculate the length. + name_end = p - 1; + name_len = name_end - name_begin; + tok->source_len = name_len; + + // Copy the token string. + tok->token_string = tal_strndup(tok, name_begin, name_len); + + goto newchar; + + } else if (DIGIT(c) || c == '-') { + + // Number tokens. + const char *num_start = p - 1; + char type = 'i'; + + if (c == '-') { + c = *p++; + if (!DIGIT(c)) + return "negative sign must precede a number"; + } + + if (c == '0') { + c = *p++; + if (DIGIT(c)) + return "leading zeros are not allowed"; + } else { + do { + c = *p++; + } while(DIGIT(c)); + } + + if (c == '.') { + type = 'f'; + if (!DIGIT(*p)) + return "invalid float value fractional part"; + do { + c = *p++; + } while(DIGIT(c)); + } + + if (c == 'e' || c == 'E') { + type = 'f'; + c = *p++; + if (c == '+' || c == '-') + c = *p++; + if (!DIGIT(*p)) + return "invalid float value exponent part"; + do { + c = *p++; + } while(DIGIT(c)); + } + + if (c == '.' || NAME_START(c)) + return "invalid numeric value"; + + const char *num_end = p - 1; + int num_len = num_end - num_start; + + tok = tal(tok_list, struct graphql_token); + list_add_tail(tok_list, tok); + tok->token_type = type; + tok->token_string = tal_strndup(tok, num_start, num_len); + tok->source_line = line_num; + tok->source_column = num_start - line_beginning + 1; + tok->source_len = num_len; + + goto newchar; + + } else if (c == '"') { + + // String tokens. + c = *p++; + const char *str_begin = p - 1; + const char *str_end; + bool str_block = false; + if (c == '"') { + c = *p++; + if (c == '"') { + // block string + str_block = true; + str_begin += 2; + int quotes = 0; + do { + c = *p++; + if (c == '\"') quotes++; else quotes = 0; + if (quotes == 3 && *(p-4) == '\\') quotes = 0; + } while (BLOCK_STRING_CHAR(c) && quotes < 3); + if (quotes == 3) { + c = *--p; + c = *--p; + } + str_end = p - 1; + if (c != '"') + return "unterminated string or invalid character"; + c = *p++; + if (c != '"') + return "invalid string termination"; + c = *p++; + if (c != '"') + return "invalid string termination"; + } else { + // empty string + str_end = str_begin; + --p; + } + } else { + // normal string + --p; + do { + c = *p++; + if (c == '\\') { + c = *p++; + if (strchr("\"\\/bfnrtu", c)) { + if (c == 'u') { + c = *p++; + if (!HEX_DIGIT(c)) + return "invalid unicode escape sequence"; + c = *p++; + if (!HEX_DIGIT(c)) + return "invalid unicode escape sequence"; + c = *p++; + if (!HEX_DIGIT(c)) + return "invalid unicode escape sequence"; + c = *p++; + if (!HEX_DIGIT(c)) + return "invalid unicode escape sequence"; + } else { + c = 'a'; // anything besides a quote to let the loop continue + } + } else { + return "invalid string escape sequence"; + } + } + } while (STRING_CHAR(c)); + if (c != '"') + return "unterminated string or invalid character"; + str_end = p - 1; + } + int str_len = str_end - str_begin; + + tok = tal(tok_list, struct graphql_token); + list_add_tail(tok_list, tok); + tok->token_type = 's'; + tok->token_specific = 's'; + tok->token_string = tal_strndup(tok, str_begin, str_len); + tok->source_line = line_num; + tok->source_column = str_begin - line_beginning + 1; + tok->source_len = str_len; + + // Process escape sequences. These always shorten the string (so the memory allocation is always enough). + char d; + char *q = tok->token_string; + char *rewrite_dest; + int quotes = 0; + while (d = *q++) { + if (str_block) { + if (d == '\"') quotes++; else quotes = 0; + if (quotes == 3 && *(q-4) == '\\') { + quotes = 0; + rewrite_dest = q - 4; + strcpy(rewrite_dest, q - 3); + } + } else { + if (d == '\\') { + rewrite_dest = q - 1; + d = *q++; + switch (d) { + case '\"': + *rewrite_dest++ = '\"'; + strcpy(rewrite_dest, q--); + break; + case 'b': + *rewrite_dest++ = '\b'; + strcpy(rewrite_dest, q--); + break; + case 'f': + *rewrite_dest++ = '\f'; + strcpy(rewrite_dest, q--); + break; + case 'n': + *rewrite_dest++ = '\n'; + strcpy(rewrite_dest, q--); + break; + case 'r': + *rewrite_dest++ = '\r'; + strcpy(rewrite_dest, q--); + break; + case 't': + *rewrite_dest++ = '\t'; + strcpy(rewrite_dest, q--); + break; + case 'u': { + // Insert escaped character using UTF-8 multi-byte encoding. + char buf[] = {*q++, *q++, *q++, *q++, 0}; + int code_point = strtol(buf, 0, 16); + int bytes = utf8_encode(code_point, rewrite_dest); + rewrite_dest += bytes; + strcpy(rewrite_dest, q--); + } + break; + default: + strcpy(rewrite_dest, --q); + } + } + } + } + if (str_block) { + // Strip leading lines. + q = tok->token_string; + for (;;) { + d = *q++; + while (WHITE_SPACE(d)) + d = *q++; + if (LINE_TERMINATOR(d)) { + while (LINE_TERMINATOR(d)) + d = *q++; + strcpy(tok->token_string, q - 1); + q = tok->token_string; + } else + break; + } + + // Strip trailing lines. + q = tok->token_string + strlen(tok->token_string); + for (;;) { + d = *--q; + while (WHITE_SPACE(d)) + d = *--q; + if (LINE_TERMINATOR(d)) { + while (LINE_TERMINATOR(d)) + d = *--q; + *++q = 0; + } else + break; + } + + // Look for common indentation. + char *this_indent_start; + const char *this_indent_end; + const char *common_indent_start = NULL; + const char *common_indent_end; + const char *r; + q = tok->token_string; + do { + d = *q++; + this_indent_start = q - 1; + while (WHITE_SPACE(d)) + d = *q++; + this_indent_end = q - 1; + if (LINE_TERMINATOR(d)) { + while (LINE_TERMINATOR(d)) + d = *q++; + continue; + } + if (EOF_CHAR(d)) + continue; + + if (common_indent_start == NULL) { + common_indent_start = this_indent_start; + common_indent_end = this_indent_end; + } + for (r = this_indent_start; r < this_indent_end && (r - this_indent_start + common_indent_start < common_indent_end); r++) { + if (*r != *(r - this_indent_start + common_indent_start)) + break; + } + common_indent_end = r - this_indent_start + common_indent_start; + + while (!LINE_TERMINATOR(d) && !EOF_CHAR(d)) + d = *q++; + while (LINE_TERMINATOR(d)) + d = *q++; + --q; + + } while (d); + + // Remove common indentation. + int common_indent_len = common_indent_end - common_indent_start; + if (common_indent_len > 0) { + q = tok->token_string; + do { + d = *q++; + this_indent_start = q - 1; + while (WHITE_SPACE(d)) + d = *q++; + this_indent_end = q - 1; + if (LINE_TERMINATOR(d)) { + while (LINE_TERMINATOR(d)) + d = *q++; + continue; + } + if (EOF_CHAR(d)) + continue; + + while (!LINE_TERMINATOR(d) && !EOF_CHAR(d)) + d = *q++; + --q; + + strcpy(this_indent_start, this_indent_start + common_indent_len); + q -= common_indent_len; + d = *q++; + + while (LINE_TERMINATOR(d)) + d = *q++; + --q; + + } while (d); + } + } + c = *p++; + goto newchar; + + } else { + return "invalid source character encountered"; + } + + } while (!EOF_CHAR(c)); + + return "unexpected end-of-input encountered"; +} + + + diff --git a/ccan/graphql/graphql.h b/ccan/graphql/graphql.h new file mode 100644 index 00000000..94f7fa4d --- /dev/null +++ b/ccan/graphql/graphql.h @@ -0,0 +1,229 @@ +#ifndef __GRAPHQL_H__ +#define __GRAPHQL_H__ 1 + +#include + +#include +#include + +// Coding constants +#define GRAPHQL_SUCCESS 0 + +// The following structures constitute the AST returned by the parser. + +struct graphql_directive { + struct graphql_directive *next; + struct graphql_token *name; + struct graphql_arguments *args; +}; + +struct graphql_directives { + struct graphql_directive *first; +}; + +struct graphql_named_type { + struct graphql_token *name; +}; + +struct graphql_type { + struct graphql_named_type *named; +// struct graphql_list_type *list; +// struct graphql_non_null_type *non_null; +}; + +struct graphql_default_value { + struct graphql_value *val; +}; + +struct graphql_variable_definition { + struct graphql_variable_definition *next; + struct graphql_variable *var; + struct graphql_type *type; + struct graphql_default_value *default_val; + struct graphql_directives *directives; +}; + +struct graphql_variable_definitions { + struct graphql_variable_definition *first; +}; + +struct graphql_variable { + struct graphql_token *name; +}; + +struct graphql_object_field { + struct graphql_object_field *next; + struct graphql_token *name; + struct graphql_value *val; +}; + +struct graphql_object_value { + struct graphql_object_field *first; +}; + +struct graphql_list_value { + struct graphql_token *val; +}; + +struct graphql_enum_value { + struct graphql_token *val; +}; + +struct graphql_null_value { + struct graphql_token *val; +}; + +struct graphql_string_value { + struct graphql_token *val; +}; + +struct graphql_boolean_value { + struct graphql_token *val; +}; + +struct graphql_float_value { + struct graphql_token *val; +}; + +struct graphql_int_value { + struct graphql_token *val; +}; + +struct graphql_value { + struct graphql_variable *var; + struct graphql_int_value *int_val; + struct graphql_float_value *float_val; + struct graphql_boolean_value *bool_val; + struct graphql_string_value *str_val; + struct graphql_null_value *null_val; + struct graphql_enum_value *enum_val; + struct graphql_list_value *list_val; + struct graphql_object_value *obj_val; +}; + +struct graphql_inline_fragment { + struct graphql_type_condition *type_cond; + struct graphql_directives *directives; + struct graphql_selection_set *sel_set; +}; + +struct graphql_type_condition { + struct graphql_named_type *named_type; +}; + +struct graphql_fragment_name { + struct graphql_token *name; +}; + +struct graphql_fragment_definition { + struct graphql_fragment_name *name; + struct graphql_type_condition *type_cond; + struct graphql_directives *directives; + struct graphql_selection_set *sel_set; +}; + +struct graphql_fragment_spread { + struct graphql_fragment_name *name; + struct graphql_directives *directives; +}; + +struct graphql_alias { + struct graphql_token *name; +}; + +struct graphql_argument { + struct graphql_argument *next; + struct graphql_token *name; + struct graphql_value *val; +}; + +struct graphql_arguments { + struct graphql_argument *first; +}; + +struct graphql_field { + struct graphql_alias *alias; + struct graphql_token *name; + struct graphql_arguments *args; + struct graphql_directives *directives; + struct graphql_selection_set *sel_set; +}; + +struct graphql_selection { + struct graphql_selection *next; + struct graphql_field *field; + struct graphql_fragment_spread *frag_spread; + struct graphql_inline_fragment *inline_frag; +}; + +struct graphql_selection_set { + struct graphql_selection *first; +}; + +struct graphql_operation_type { + struct graphql_token *op_type; +}; + +struct graphql_operation_definition { + struct graphql_operation_type *op_type; + struct graphql_token *op_name; + struct graphql_variable_definitions *vars; + struct graphql_directives *directives; + struct graphql_selection_set *sel_set; +}; + +struct graphql_executable_definition { + struct graphql_executable_definition *next_def; + struct graphql_operation_definition *op_def; + struct graphql_fragment_definition *frag_def; +}; + +struct graphql_executable_document { + struct graphql_executable_definition *first_def; +}; + +struct graphql_definition { + struct graphql_definition *next_def; + struct graphql_executable_definition *executable_def; + struct graphql_type_system_definition_or_extension *type_system_def; +}; + +struct graphql_document { + struct graphql_definition *first_def; +}; + + +struct graphql_token { + struct list_node list; + unsigned int token_type; + unsigned int token_specific; + char *token_string; + unsigned int source_line; + unsigned int source_column; + unsigned int source_len; +}; + +/* The lexer. + * INPUTS: + * input - string to parse + * ctx - parent tal context or NULL + * tokens - a variable to receive the resulting token list + * RETURN: + * GRAPHQL_SUCCESS or an error string. + */ +const char *graphql_lex(const char *input, const tal_t *ctx, struct list_head **tokens); + +/* The parser. + * INPUTS: + * tokens - the list produced by the lexer + * doc - a variable to receive the resulting abstract syntax tree (AST) + * RETURN: + * GRAPHQL_SUCCESS or an error string. + */ +const char *graphql_parse(struct list_head *tokens, struct graphql_executable_document **doc); + +/* The lexer and parser in one function, for convenience. */ +const char *graphql_lexparse(const char *input, const tal_t *ctx, struct list_head **tokens, struct graphql_executable_document **doc); + +#endif + diff --git a/ccan/graphql/test/run.c b/ccan/graphql/test/run.c new file mode 100644 index 00000000..91bc322e --- /dev/null +++ b/ccan/graphql/test/run.c @@ -0,0 +1,3177 @@ +#include "ccan/graphql/graphql.h" +#include "ccan/str/str.h" + +/* TEST POINT MACROS + * + * The idea here is to run each test case silently, and if any test point + * fails, that test case is re-run verbosely to pinpoint the failure. + * + * RUN macros run a whole test case function. + * + * Different TEST_xxxx macros are provided for different test point types as + * follows: + * + * TEST_CONT - Test and continue testing regardless of failure (the failure + * will still be printed). + * TEST_ABRT - Test and abort the test case, used when testing pointer + * validity to avoid subsequent dereference errors. + * TEST_STRG - Test a string value, which includes the convenience of testing + * for a null pointer. + */ + +#define RUN(test) { prev_fail = fail; test(source); if (fail != prev_fail) { mute = false; test(source); mute = true; } } +#define RUN1(test,arg) { prev_fail = fail; test(source,arg); if (fail != prev_fail) { mute = false; test(source,arg); mute = true; } } +#define RUN2(test,arg1,arg2) { prev_fail = fail; test(source,arg1,arg2); if (fail != prev_fail) { mute = false; test(source,arg1,arg2); mute = true; } } + +#define TEST_CONT(expr) { bool c = (expr); if (mute) c? pass++ : fail++; else printf("%s: %s\033[0m\n", c? "passed" : "\033[91mfailed", stringify(expr)); } +#define TEST_ABRT(expr) { bool c = (expr); if (mute) c? pass++ : fail++; else printf("%s: %s\033[0m\n", c? "passed" : "\033[91mfailed", stringify(expr)); if (!c) return; } +#define TEST_STRG(str,expr) { bool c = ((str) && streq((str),(expr))); if (mute) c? pass++ : fail++; else printf("%s: %s == %s\033[0m\n", c? "passed" : "\033[91mfailed", stringify(str), stringify(expr)); if (!c) return; } + +// Global variables to track overall results. +int pass = 0, fail = 0; +bool mute = 1; + +// Helper function. +int listlen(struct list_head *tokens) { + struct graphql_token *tok; + int n=0; + list_for_each(tokens, tok, list) { + n++; + } + return n; +} + +/* Test case functions begin here, called by main(). + * Note: Memory should be freed correctly in the success case, but if there + * are errors, all bets are off. + */ + +int check_example_3(const char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 3\n"); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '{'); + TEST_CONT(tok->source_line == 1); + TEST_CONT(tok->source_column == 1); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 4); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '('); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 7); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 8); + TEST_CONT(tok->source_len == 2); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == ':'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 10); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "4"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == ')'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 13); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '{'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 15); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + TEST_CONT(tok->source_line == 3); + TEST_CONT(tok->source_column == 5); + TEST_CONT(tok->source_len == 4); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '}'); + TEST_CONT(tok->source_line == 4); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '}'); + TEST_CONT(tok->source_line == 5); + TEST_CONT(tok->source_column == 1); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "name"); + tokens = tal_free(tokens); +} + +int check_example_5(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 5\n"); + + sprintf(source, "\ +mutation {\n\ + likeStory(storyID: 12345) {\n\ + story {\n\ + likeCount\n\ + }\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 15); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "mutation"); + TEST_CONT(tok->source_line == 1); + TEST_CONT(tok->source_column == 1); + TEST_CONT(tok->source_len == 8); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "likeStory"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 9); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "storyID"); + TEST_CONT(tok->source_line == 2); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "12345"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 22); + TEST_CONT(tok->source_len == 5); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "story"); + TEST_CONT(tok->source_line == 3); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "likeCount"); + TEST_CONT(tok->source_line == 4); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_ABRT(doc->first_def->op_def->op_type != NULL); + TEST_ABRT(doc->first_def->op_def->op_type->op_type != NULL); + TEST_CONT(doc->first_def->op_def->op_type->op_type->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->op_type->op_type->token_string, "mutation"); + TEST_ABRT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "likeStory"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "storyID"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "12345"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "story"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->name->token_string, "likeCount"); + tokens = tal_free(tokens); +} + +int check_example_6(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 6\n"); + + sprintf(source, "\ +{\n\ + field\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 3); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "field"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 5); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_ABRT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "field"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL); + tokens = tal_free(tokens); +} + +int check_example_7(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 7\n"); + + sprintf(source, "\ +{\n\ + id\n\ + firstName\n\ + lastName\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 5); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 2); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "firstName"); + TEST_CONT(tok->source_line == 3); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 9); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "lastName"); + TEST_CONT(tok->source_line == 4); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 8); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); + TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_ABRT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->next->field->name->token_string, "firstName"); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->next->next->field->name->token_string, "lastName"); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->next->next == NULL); + tokens = tal_free(tokens); +} + +int check_example_8(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 8\n"); + + sprintf(source, "\ +{\n\ + me {\n\ + id\n\ + firstName\n\ + lastName\n\ + birthday {\n\ + month\n\ + day\n\ + }\n\ + friends {\n\ + name\n\ + }\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 17); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "me"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "firstName"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "lastName"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "birthday"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "month"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "day"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "friends"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_ABRT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "me"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "firstName"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "lastName"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name->token_string, "birthday"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->name->token_string, "month"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->name->token_string, "day"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->name->token_string, "friends"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->next == NULL); + tokens = tal_free(tokens); +} + +int check_example_9(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 9\n"); + + sprintf(source, "\ +# `me` could represent the currently logged in viewer.\n\ +{\n\ + me {\n\ + name\n\ + }\n\ +}\n\ +\n\ +# `user` represents one of many users in a graph of data, referred to by a\n\ +# unique identifier.\n\ +{\n\ + user(id: 4) {\n\ + name\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 17); + // NOTE: Comments are ignored. + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "me"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "4"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->next_def != NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_ABRT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "me"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->next_def->next_def == NULL); + TEST_CONT(doc->first_def->next_def->frag_def == NULL); + TEST_ABRT(doc->first_def->next_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->next_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->next_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->next_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "name"); + tokens = tal_free(tokens); +} + +int check_example_10(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 10\n"); + + sprintf(source, "\ +{\n\ + user(id: 4) {\n\ + id\n\ + name\n\ + profilePic(size: 100)\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 18); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "4"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "profilePic"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "size"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "100"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "profilePic"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_string, "size"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_string, "100"); + tokens = tal_free(tokens); +} + +int check_example_11(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 11\n"); + + sprintf(source, "\ +{\n\ + user(id: 4) {\n\ + id\n\ + name\n\ + profilePic(width: 100, height: 50)\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 21); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "4"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "profilePic"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "width"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "100"); + // NOTE: Comma is ignored. + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "height"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "50"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "profilePic"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_string, "width"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_string, "100"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->name->token_string, "height"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val->int_val->val->token_string, "50"); + tokens = tal_free(tokens); +} + +int check_example_12_and_13(const char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example Nos. 12 and 13\n"); + + // Test the lexer. + const char *param; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "picture"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_ABRT(tok->token_string != NULL && (streq(tok->token_string, "width") || streq(tok->token_string, "height"))); + param = tok->token_string; + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_CONT(tok->token_string != NULL && ((streq(param, "width") && streq(tok->token_string, "200")) || (streq(param, "height") && streq(tok->token_string, "100")))); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_CONT(tok->token_string != NULL && (streq(tok->token_string, "width") || streq(tok->token_string, "height"))); + param = tok->token_string; + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_CONT(tok->token_string != NULL && ((streq(param, "width") && streq(tok->token_string, "200")) || (streq(param, "height") && streq(tok->token_string, "100")))); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_argument *arg; + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "picture"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next->next == NULL); + arg = doc->first_def->op_def->sel_set->first->field->args->first; + if (!streq(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "width")) arg = arg->next; + TEST_ABRT(arg->name != NULL); + TEST_CONT(arg->name->token_type == 'a'); + TEST_STRG(arg->name->token_string, "width"); + TEST_ABRT(arg->val != NULL); + TEST_ABRT(arg->val->int_val != NULL); + TEST_ABRT(arg->val->int_val->val != NULL); + TEST_CONT(arg->val->int_val->val->token_type == 'i'); + TEST_STRG(arg->val->int_val->val->token_string, "200"); + arg = doc->first_def->op_def->sel_set->first->field->args->first; + if (!streq(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "height")) arg = arg->next; + TEST_ABRT(arg->name != NULL); + TEST_CONT(arg->name->token_type == 'a'); + TEST_STRG(arg->name->token_string, "height"); + TEST_ABRT(arg->val != NULL); + TEST_ABRT(arg->val->int_val != NULL); + TEST_ABRT(arg->val->int_val->val != NULL); + TEST_CONT(arg->val->int_val->val->token_type == 'i'); + TEST_STRG(arg->val->int_val->val->token_string, "100"); + tokens = tal_free(tokens); +} + +int check_example_14(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 14\n"); + + sprintf(source, "\ +{\n\ + user(id: 4) {\n\ + id\n\ + name\n\ + smallPic: profilePic(size: 64)\n\ + bigPic: profilePic(size: 1024)\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 28); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "4"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "smallPic"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "profilePic"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "size"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "64"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "bigPic"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "profilePic"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "size"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "1024"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias->name->token_string, "smallPic"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "profilePic"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_string, "size"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_string, "64"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias->name->token_string, "bigPic"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name->token_string, "profilePic"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->name->token_string, "size"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val->int_val->val->token_string, "1024"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->next == NULL); + tokens = tal_free(tokens); +} + +int check_example_16(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 16\n"); + + sprintf(source, "\ +{\n\ + zuck: user(id: 4) {\n\ + id\n\ + name\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 14); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "zuck"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "4"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->alias != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->alias->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->alias->name->token_string, "zuck"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next == NULL); + tokens = tal_free(tokens); +} + +int check_example_18(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 18\n"); + + sprintf(source, "\ +query noFragments {\n\ + user(id: 4) {\n\ + friends(first: 10) {\n\ + id\n\ + name\n\ + profilePic(size: 50)\n\ + }\n\ + mutualFriends(first: 10) {\n\ + id\n\ + name\n\ + profilePic(size: 50)\n\ + }\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 44); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next == NULL); + tokens = tal_free(tokens); +} + +int check_example_19(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 19\n"); + + sprintf(source, "\ +query withFragments {\n\ + user(id: 4) {\n\ + friends(first: 10) {\n\ + ...friendFields\n\ + }\n\ + mutualFriends(first: 10) {\n\ + ...friendFields\n\ + }\n\ + }\n\ +}\n\ +\n\ +fragment friendFields on User {\n\ + id\n\ + name\n\ + profilePic(size: 50)\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 46); + for (int i=0; i<17; i++) + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 0x2026); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "friendFields"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + for (int i=0; i<7; i++) + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 0x2026); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "friendFields"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + const char *e; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL); + if (e) printf("%s\n", e); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name->token_string, "friendFields"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name->token_string, "friendFields"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next == NULL); + TEST_ABRT(doc->first_def->next_def != NULL); + TEST_CONT(doc->first_def->next_def->next_def == NULL); + TEST_CONT(doc->first_def->next_def->op_def == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->name != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->name->name != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->frag_def->name->name->token_string, "friendFields"); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_string, "User"); + TEST_CONT(doc->first_def->next_def->frag_def->directives == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next->next == NULL); + tokens = tal_free(tokens); +} + +int check_example_20(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 20\n"); + + sprintf(source, "\ +query withNestedFragments {\n\ + user(id: 4) {\n\ + friends(first: 10) {\n\ + ...friendFields\n\ + }\n\ + mutualFriends(first: 10) {\n\ + ...friendFields\n\ + }\n\ + }\n\ +}\n\ +\n\ +fragment friendFields on User {\n\ + id\n\ + name\n\ + ...standardProfilePic\n\ +}\n\ +\n\ +fragment standardProfilePic on User {\n\ + profilePic(size: 50)\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 54); + for (int i=0; i<17; i++) + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 0x2026); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "friendFields"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + for (int i=0; i<7; i++) + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 0x2026); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "friendFields"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + const char *e; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL); + if (e) printf("%s\n", e); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name->token_string, "friendFields"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name->token_string, "friendFields"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next == NULL); + TEST_ABRT(doc->first_def->next_def != NULL); + TEST_CONT(doc->first_def->next_def->op_def == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->name != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->name->name != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->frag_def->name->name->token_string, "friendFields"); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_string, "User"); + TEST_CONT(doc->first_def->next_def->frag_def->directives == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->next->next->field == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next->frag_spread != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next->next == NULL); + TEST_ABRT(doc->first_def->next_def->next_def != NULL); + TEST_CONT(doc->first_def->next_def->next_def->next_def == NULL); + TEST_CONT(doc->first_def->next_def->next_def->op_def == NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->name != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->name->name != NULL); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->next_def->frag_def->name->name->token_string, "standardProfilePic"); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name->token_string, "User"); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->directives == NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->sel_set->first->next == NULL); + tokens = tal_free(tokens); +} + +int check_example_21(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 21\n"); + + sprintf(source, "\ +query FragmentTyping {\n\ + profiles(handles: [\"zuck\", \"coca-cola\"]) {\n\ + handle\n\ + ...userFragment\n\ + ...pageFragment\n\ + }\n\ +}\n\ +\n\ +fragment userFragment on User {\n\ + friends {\n\ + count\n\ + }\n\ +}\n\ +\n\ +fragment pageFragment on Page {\n\ + likers {\n\ + count\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 40); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "query"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "FragmentTyping"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "profiles"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "handles"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '['); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 's'); + TEST_STRG(tok->token_string, "zuck"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 's'); + TEST_STRG(tok->token_string, "coca-cola"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ']'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + const char *e; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL); + if (e) printf("%s\n", e); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread->name != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread->name->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread->name->name->token_string, "userFragment"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread->name != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread->name->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread->name->name->token_string, "pageFragment"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_ABRT(doc->first_def->next_def != NULL); + TEST_CONT(doc->first_def->next_def->op_def == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->name != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->name->name != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->frag_def->name->name->token_string, "userFragment"); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_string, "User"); + TEST_CONT(doc->first_def->next_def->frag_def->directives == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->next_def->next_def != NULL); + TEST_CONT(doc->first_def->next_def->next_def->next_def == NULL); + TEST_CONT(doc->first_def->next_def->next_def->op_def == NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->name != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->name->name != NULL); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->next_def->frag_def->name->name->token_string, "pageFragment"); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name->token_string, "Page"); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->directives == NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->sel_set->first->next == NULL); + tokens = tal_free(tokens); +} + +int check_example_23(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 23\n"); + + sprintf(source, "\ +query inlineFragmentTyping {\n\ + profiles(handles: [\"zuck\", \"coca-cola\"]) {\n\ + handle\n\ + ... on User {\n\ + friends {\n\ + count\n\ + }\n\ + }\n\ + ... on Page {\n\ + likers {\n\ + count\n\ + }\n\ + }\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 34); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "query"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "inlineFragmentTyping"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "profiles"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "handles"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '['); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 's'); + TEST_STRG(tok->token_string, "zuck"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 's'); + TEST_STRG(tok->token_string, "coca-cola"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ']'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + const char *e; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL); + if (e) printf("%s\n", e); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->type_cond != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->type_cond->named_type->name->token_string, "User"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond->named_type->name->token_string, "Page"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->next_def == NULL); + tokens = tal_free(tokens); +} + +int check_example_24(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 24\n"); + + sprintf(source, "\ +query inlineFragmentNoType($expandedInfo: Boolean) {\n\ + user(handle: \"zuck\") {\n\ + id\n\ + name\n\ + ... @include(if: $expandedInfo) {\n\ + firstName\n\ + lastName\n\ + birthday\n\ + }\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 34); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "query"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "inlineFragmentNoType"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '$'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "expandedInfo"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "Boolean"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "handle"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 's'); + TEST_STRG(tok->token_string, "zuck"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 0x2026); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '@'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "include"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "if"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '$'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "expandedInfo"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "firstName"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "lastName"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "birthday"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->name->token_string, "include"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->name->token_string, "if"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->int_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->float_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->bool_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->str_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->null_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->enum_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->list_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->obj_val == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->var != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->var->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->var->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->var->name->token_string, "expandedInfo"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->next_def == NULL); + tokens = tal_free(tokens); +} + +int check_int_value(char *source, int int_value) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Int Value Range Check on %d\n", int_value); + + sprintf(source, "\ +{\n\ + user(id: %d) {\n\ + name\n\ + }\n\ +}\n\ + ", int_value); + + char buf[20]; + sprintf(buf, "%d", int_value); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, buf); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12); + TEST_CONT(tok->source_len == strlen(buf)); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12 + strlen(buf)); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, buf); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + tokens = tal_free(tokens); +} + +int check_invalid_int_values(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Invalid Int Values\n"); + + char *bad_values[] = {"00", "-00", "+1", "1.", "1a", "1e", "0x123", "123L", 0}; + + for (int i=0; bad_values[i]; i++) { + sprintf(source, "\ +{\n\ + user(id: %s) {\n\ + name\n\ + }\n\ +}\n\ + ", bad_values[i]); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) != NULL); + TEST_ABRT(listlen(tokens) == 5); + tokens = tal_free(tokens); + + // No need to test parser when lexer fails. + } +} + +int check_float_value(char *source, float float_value, const char *format) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Float Value Range Check on %f\n", float_value); + + char buf[100]; + sprintf(buf, "\ +{\n\ + user(id: %s) {\n\ + name\n\ + }\n\ +}\n\ + ", format); + sprintf(source, buf, float_value); + sprintf(buf, format, float_value); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'f'); + TEST_STRG(tok->token_string, buf); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12); + TEST_CONT(tok->source_len == strlen(buf)); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12 + strlen(buf)); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val->token_type == 'f'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val->token_string, buf); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + tokens = tal_free(tokens); +} + +int check_valid_float_values(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Valid Float Values\n"); + + char *good_values[] = {"1.0", "1e50", "6.0221413e23", "1.23", 0}; + + for (int i=0; good_values[i]; i++) { + sprintf(source, "\ +{\n\ + user(id: %s) {\n\ + name\n\ + }\n\ +}\n\ + ", good_values[i]); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'f'); + TEST_STRG(tok->token_string, good_values[i]); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12); + TEST_CONT(tok->source_len == strlen(good_values[i])); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12 + strlen(good_values[i])); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val->token_type == 'f'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val->token_string, good_values[i]); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + tokens = tal_free(tokens); + } +} + +int check_invalid_float_values(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Invalid Float Values\n"); + + char *bad_values[] = {"00.0", "-00.0", "00e1", "1.23.4", "0x1.2p3", 0}; + + for (int i=0; bad_values[i]; i++) { + sprintf(source, "\ +{\n\ + user(id: %s) {\n\ + name\n\ + }\n\ +}\n\ + ", bad_values[i]); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) != NULL); + TEST_ABRT(listlen(tokens) == 5); + tokens = tal_free(tokens); + + // No need to test parser when lexer fails. + } +} + +int check_boolean_values(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Boolean Values\n"); + + char *good_values[] = {"true", "false", 0}; + + for (int i=0; good_values[i]; i++) { + sprintf(source, "\ +{\n\ + user(id: %s) {\n\ + name\n\ + }\n\ +}\n\ + ", good_values[i]); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val->val->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val->val->token_string, good_values[i]); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + tokens = tal_free(tokens); + } + + char *bad_values[] = {"True", "False", "TRUE", "FALSE", 0}; + + for (int i=0; bad_values[i]; i++) { + sprintf(source, "\ +{\n\ + user(id: %s) {\n\ + name\n\ + }\n\ +}\n\ + ", bad_values[i]); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tokens = tal_free(tokens); + + // Test the parser (it will succeed in parsing the bad values as enum values, not boolean values). + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->var == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->list_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val->val->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val->val->token_string, bad_values[i]); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + tokens = tal_free(tokens); + } +} + +int check_string_value(char *source, const char *test_value, const char *expected_result) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// String Value Test: %s\n", test_value); + + sprintf(source, "\ +{\n\ + user(id:%s) {\n\ + name\n\ + }\n\ +}\n\ + ", test_value); + + bool block = (test_value[0]=='\"' && test_value[1]=='\"' && test_value[2]=='\"')? true: false; + if (expected_result) { + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 's'); + TEST_STRG(tok->token_string, expected_result); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 11 + (block? 3: 1)); + TEST_CONT(tok->source_len == strlen(test_value) - (block? 6: 2)); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 11 + strlen(test_value)); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser (it will succeed in parsing the bad values as enum values, not boolean values). + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->var == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->list_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val->val->token_type == 's'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val->val->token_string, expected_result); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + tokens = tal_free(tokens); + } else { + TEST_CONT(graphql_lex(source, NULL, &tokens) != NULL); + tokens = tal_free(tokens); + } +} + +int check_example_25_and_26(const char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 25 and 26\n"); + + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + while ((tok = list_pop(tokens, struct graphql_token, list)) && tok->token_type != 's') { } + if (tok) { + TEST_STRG(tok->token_string, "Hello,\n World!\n\nYours,\n GraphQL."); + } + + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + tokens = tal_free(tokens); +} + +int check_example_29(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 29\n"); + + sprintf(source, "\ +{\n\ + field(arg: null)\n\ + field\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 9); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "null"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "field"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "arg"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->var == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->list_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val->val->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val->val->token_string, "null"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->next->field->name->token_string, "field"); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->args == NULL); + tokens = tal_free(tokens); +} + +int check_example_30_and_31(const char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 30 and 31\n"); + + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 15); + while ((tok = list_pop(tokens, struct graphql_token, list)) && !(tok->token_type == 'a' && tok->token_string != NULL && streq(tok->token_string, "lat"))) { } + TEST_CONT(tok); + if (tok) { + TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'f'); + TEST_STRG(tok->token_string, "-53.211"); + } + tokens = tal_free(tokens); + + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + while ((tok = list_pop(tokens, struct graphql_token, list)) && !(tok->token_type == 'a' && tok->token_string != NULL && streq(tok->token_string, "lon"))) { } + TEST_CONT(tok); + if (tok) { + TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'f'); + TEST_STRG(tok->token_string, "12.43"); + } + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_argument *arg; + struct graphql_executable_document *doc; + const char *e; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL); + if (e) printf("%s\n", e); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "nearestThing"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "location"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->var == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->list_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->name->token_type == 'a'); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->val->float_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->val->float_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->val->float_val->val->token_type == 'f'); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->name->token_type == 'a'); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->val->float_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->val->float_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->val->float_val->val->token_type == 'f'); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->next == NULL); + tokens = tal_free(tokens); +} + +int check_example_32(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 32\n"); + + sprintf(source, "\ +query getZuckProfile($devicePicSize: Int) {\n\ + user(id: 4) {\n\ + id\n\ + name\n\ + profilePic(size: $devicePicSize)\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 27); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '$'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'i'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '$'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_ABRT(doc->first_def->op_def->op_type != NULL); + TEST_ABRT(doc->first_def->op_def->op_type->op_type != NULL); + TEST_CONT(doc->first_def->op_def->op_type->op_type->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->op_type->op_type->token_string, "query"); + TEST_CONT(doc->first_def->op_def->op_name != NULL); + TEST_CONT(doc->first_def->op_def->op_name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->op_name->token_string, "getZuckProfile"); + TEST_ABRT(doc->first_def->op_def->vars != NULL); + TEST_ABRT(doc->first_def->op_def->vars->first != NULL); + TEST_CONT(doc->first_def->op_def->vars->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->vars->first->var != NULL); + TEST_ABRT(doc->first_def->op_def->vars->first->var->name != NULL); + TEST_CONT(doc->first_def->op_def->vars->first->var->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->vars->first->var->name->token_string, "devicePicSize"); + TEST_ABRT(doc->first_def->op_def->vars->first->type != NULL); +// TEST_CONT(doc->first_def->op_def->vars->first->type->list == NULL); +// TEST_CONT(doc->first_def->op_def->vars->first->type->non_null == NULL); + TEST_ABRT(doc->first_def->op_def->vars->first->type->named != NULL); + TEST_ABRT(doc->first_def->op_def->vars->first->type->named->name != NULL); + TEST_CONT(doc->first_def->op_def->vars->first->type->named->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->vars->first->type->named->name->token_string, "Int"); + TEST_CONT(doc->first_def->op_def->vars->first->default_val == NULL); + TEST_CONT(doc->first_def->op_def->vars->first->directives == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "profilePic"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_string, "size"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->var != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->var->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->var->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->var->name->token_string, "devicePicSize"); + tokens = tal_free(tokens); +} + +int check_example_34(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 34\n"); + + sprintf(source, "\ +type Person\n\ + @addExternalFields(source: \"profiles\")\n\ + @excludeField(name: \"photo\") {\n\ + name: String\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 21); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '@'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "addExternalFields"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 's'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '@'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "excludeField"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 's'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // The type system is not yet implemented, so parsing will fail here. + // This could be "phase 2" of this project. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) != NULL); + tokens = tal_free(tokens); +} + +int check_example_35(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + const char *err; + + if (!mute) printf("// Example No. 35\n"); + + sprintf(source, "\ +type Person\n\ + @excludeField(name: \"photo\")\n\ + @addExternalFields(source: \"profiles\") {\n\ + name: String\n\ +}\n\ + "); + + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 21); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '@'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "excludeField"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 's'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '@'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "addExternalFields"); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 's'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, list); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // The type system is not yet implemented, so parsing will fail here. + // This could be "phase 2" of this project. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(source, NULL, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) != NULL); + tokens = tal_free(tokens); +} + +/* End of test case functions. */ + +/* Beginning of main() test run to run all test cases. */ + +int main(void) +{ + printf("\nTesting GraphQL lexer/parser...\n"); + + char source[1024]; + int prev_fail; // Used by RUNx macros. + + // Check the lexer with all valid line terminators. + char *new_line = "\n"; + char *carriage_return = "\r"; + char *carriage_return_new_line = "\r\n"; + char *line_terminators[] = { new_line, carriage_return, carriage_return_new_line }; + for (int i=0; i<3; i++) { + sprintf(source, "\ +{%s\ + user(id: 4) {%s\ + name%s\ + }%s\ +}%s\ + ", line_terminators[i], line_terminators[i], line_terminators[i], line_terminators[i], line_terminators[i]); + + RUN(check_example_3); + } + + RUN(check_example_5); // Parse a mutation operation and check results. + + RUN(check_example_6); // Parse an unnamed query and check results. + + RUN(check_example_7); // Parse multiple fields in a selection set and check results. + RUN(check_example_8); // Parse complex data structure and check results. + RUN(check_example_9); // Parse example of top-level fields and check results. + RUN(check_example_10); // Parse example with parameterized field and check results. + RUN(check_example_11); // Parse example with multiple field arguments and check results. + + // Parse examples of different parameter order and check for identical results. + sprintf(source, "\ +{\n\ + picture(width: 200, height: 100)\n\ +}\n\ + "); + RUN(check_example_12_and_13); + sprintf(source, "\ +{\n\ + picture(height: 100, width: 200)\n\ +}\n\ + "); + RUN(check_example_12_and_13); + + RUN(check_example_14); // Parse alias example and check results. + RUN(check_example_16); // Parse a top-level-field alias example and check results. + + RUN(check_example_18); // Parse example and check results. + + RUN(check_example_19); // Parse fragment example and check results. + RUN(check_example_20); // Parse another fragment example and check results. + RUN(check_example_21); // Parse fragment typing example and check results. + RUN(check_example_23); // Parse fragment typing example and check results. + RUN(check_example_24); // Parse fragment typing example and check results. + + // Parse various int values and check results. + for (int i= -15; i<= 15; i++) { RUN1(check_int_value, i); } + for (int i= -32770; i<= -32765; i++) { RUN1(check_int_value, i); } + for (int i= 32765; i<= 32770; i++) { RUN1(check_int_value, i); } + for (int i=-2147483648; i<=-2147483645; i++) { RUN1(check_int_value, i); } + for (int i= 2147483647; i>= 2147483645; i--) { RUN1(check_int_value, i); } + RUN(check_invalid_int_values); + + // Parse various float values and check results. + for (float i= -1.0; i<= 1.0; i+= 0.1) { RUN2(check_float_value, i, "%1.1f"); } + for (float i=-327.70; i<=-327.65; i+= 0.01) { RUN2(check_float_value, i, "%1.2f"); } + for (float i= 327.65; i<= 327.70; i+= 0.01) { RUN2(check_float_value, i, "%1.2f"); } + for (float i= -5e-20; i<= -1e-20; i+= 1e-20) { RUN2(check_float_value, i, "%1.0e"); } + for (float i= 5e-20; i>= 1e-20; i-= 1e-20) { RUN2(check_float_value, i, "%1.0e"); } + for (float i= 5E+20; i>= 1E+20; i-= 1E+20) { RUN2(check_float_value, i, "%1.2E"); } + for (float i=1.5E+20; i>=1.1E+20; i-=0.1E+20) { RUN2(check_float_value, i, "%1.1E"); } + RUN(check_valid_float_values); + RUN(check_invalid_float_values); + + RUN(check_boolean_values); // Parse boolean values and check results. + + // Parse various string values and check results. + RUN2(check_string_value, "te^st", NULL ); // Missing quotes (the caret makes it an invalid token for testing purposes). + RUN2(check_string_value, "\"te^st\"", "te^st" ); // A valid string. + RUN2(check_string_value, "\"\"", "" ); // An empty string is valid. + RUN2(check_string_value, "\"\"\"te^st\"\"\"", "te^st" ); // A block string. + RUN2(check_string_value, "\"te\\st\"", NULL ); // Backslashes are normally invalid. + RUN2(check_string_value, "\"te\nst\"", NULL ); // New-line characters are invalid except in block strings. + RUN2(check_string_value, "\"te\rst\"", NULL ); // New-line characters are invalid except in block strings. + RUN2(check_string_value, "\"\"\"te\nst\"\"\"", "te\nst" ); // New-line characters are valid in block strings. + RUN2(check_string_value, "\"\"\"te\rst\"\"\"", "te\rst" ); // New-line characters are valid in block strings. + RUN2(check_string_value, "\"te\"st\"", NULL ); // A quote in a string is invalid. + RUN2(check_string_value, "\"te\\\"st\"", "te\"st" ); // ...unless it is escaped. + RUN2(check_string_value, "\"\"\"te\"st\"\"\"", "te\"st" ); // A quote in a block string is valid. + RUN2(check_string_value, "\"\"\"te\"\"st\"\"\"", "te\"\"st" ); // It is even valid to have two quotes in a block string. + RUN2(check_string_value, "\"\"\"te\"\"\"st\"\"\"", NULL ); // Three quotes in a row are not allowed in a block string. + RUN2(check_string_value, "\"\"\"te\\\"\"\"st\"\"\"", "te\"\"\"st" ); // ...unless escaped. + RUN2(check_string_value, "\"te\\\"st\"", "te\"st" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\\\st\"", "te\\st" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\/st\"", "te/st" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\bst\"", "te\bst" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\fst\"", "te\fst" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\nst\"", "te\nst" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\rst\"", "te\rst" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\tst\"", "te\tst" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\vst\"", NULL ); // Invalid escape sequence. + RUN2(check_string_value, "\"te\\033st\"", NULL ); // Invalid escape sequence. + // Note: Unicode excape sequence is tested below. + + // This block string and this string should result in identical tokens. + sprintf(source, "\ +mutation {\n\ + sendEmail(message: \"\"\"\n\ + Hello,\n\ + World!\n\ +\n\ + Yours,\n\ + GraphQL.\n\ + \"\"\")\n\ +}\n\ + "); + RUN(check_example_25_and_26); + sprintf(source, "\ +mutation {\n\ + sendEmail(message: \"Hello,\\n World!\\n\\nYours,\\n GraphQL.\")\n\ +}\n\ + "); + RUN(check_example_25_and_26); + + // Check block string example. + RUN2(check_string_value, +"\"\"\"\n\ +This starts with and ends with an empty line,\n\ +which makes it easier to read.\n\ +\"\"\"", + "This starts with and ends with an empty line,\nwhich makes it easier to read."); + + // Check block string counter example. + RUN2(check_string_value, +"\"\"\"This does not start with or end with any empty lines,\n\ +which makes it a little harder to read.\"\"\"", + "This does not start with or end with any empty lines,\nwhich makes it a little harder to read."); + + RUN2(check_string_value, "\"te\\u001bst\"", "te\033st" ); // Check unicode escape sequence. + RUN2(check_string_value, "\"te\\u001Bst\"", "te\033st" ); // Check again with other case. + RUN2(check_string_value, "\"\"\"te\\u001bst\"\"\"", "te\\u001bst" ); // Escape sequences are ignored in block strings (except for the triple quote). + RUN2(check_string_value, "\"\"\"te\\nst\"\"\"", "te\\nst" ); // Escape sequences are ignored in block strings (except for the triple quote). + RUN2(check_string_value, "\"te\\u2026st\"", "te\xe2\x80\xa6st"); // Check a unicode escape sequence. + + RUN(check_example_29); // Parse null value and check result. + + // These two input objects should have the same result. + sprintf(source, "\ +{\n\ + nearestThing(location: { lon: 12.43, lat: -53.211 })\n\ +}\n\ + "); + RUN(check_example_30_and_31); + sprintf(source, "\ +{\n\ + nearestThing(location: { lat: -53.211, lon: 12.43 })\n\ +}\n\ + "); + RUN(check_example_30_and_31); + + RUN(check_example_32); // Parse an example with a variable and check result. + + RUN(check_example_34); // Parse directives and check result. + RUN(check_example_35); // Parse directives and check result. + + RUN(check_example_35); // Parse directives and check result. + + printf("total passed: %d\n%stotal failed: %d\033[0m\n", pass, fail?"\033[91m":"", fail); + + return fail==0? 0: 1; +} + -- 2.39.2