/* Check for stray non-whitespace after the end and free memory. */

static void check_end (parser_t * parser)
{
    int c;
 end:
    switch (NEXTBYTE) {

    case WHITESPACE:
	goto end;

    case '\0':
	parser_free (parser);
	return;

    default:
	parser->bad_type = json_initial_state;
	parser->bad_byte = parser->end - 1;
	parser->expected = XWHITESPACE;
	parser->error = json_error_unexpected_character;
	failbadinput (parser);
    }
}

#define ENTRYDECL				\
    /* Our collection of bits and pieces. */	\
						\
    parser_t parser_o = {0};			\
    parser_t * parser = & parser_o

#ifndef NOPERL

/* Set up the parser. */

#define GETSTRING							\
    parser_o.end = parser_o.input =					\
	(unsigned char *) SvPV (json, parser_o.length);			\
    parser->unicode = SvUTF8 (json) ? 1 : 0

#endif /* ndef NOPERL */

#define SETUPPARSER							\
    parser->line = 1;							\
    parser->last_byte = parser->input + parser->length

/* Error to throw if there is a character other than whitespace, "["
   or "{" at the start of the JSON. */

#define BADCHAR								\
    parser->bad_byte = parser->end - 1;					\
    parser->bad_type = json_initial_state;				\
    parser->expected = XARRAYOBJECTSTART | VALUE_START | XWHITESPACE;	\
    parser->error = json_error_unexpected_character;			\
    failbadinput (parser)

#ifndef NOPERL

/* This is the entry point for parsing. */

static SV *
parse (SV * json)
{
    /* The currently-parsed character. */	
						
    char c;					
						
    ENTRYDECL;

    /* The returned object. */

    SV * r = & PL_sv_undef;

    GETSTRING;

    if (parser_o.length == 0) {
	return & PL_sv_undef;
    }

    SETUPPARSER;

 parse_start:

    switch (NEXTBYTE) {

    case '{':
	r = object (& parser_o);
	break;

    case '[':
	r = array (& parser_o);
	break;

    case '-':
    case '0':
    case DIGIT19:
	parser_o.top_level_value = 1;
	r = number (& parser_o);
	break;

    case '"':
	parser_o.top_level_value = 1;
	r = string (& parser_o);
	break;

    case 't':
	parser_o.top_level_value = 1;
	r = literal_true (& parser_o);
	break;

    case 'f':
	parser_o.top_level_value = 1;
	r = literal_false (& parser_o);
	break;

    case 'n':
	parser_o.top_level_value = 1;
	r = literal_null (& parser_o);
	break;

    case WHITESPACE:
	goto parse_start;

    case '\0':

	/* We have an empty string. */

	r = & PL_sv_undef;

	/* So there is no point checking that the string doesn't
	   contain junk characters after its end. */

	goto noendcheck;

    default:
	BADCHAR;
    }

    check_end (parser);

 noendcheck:

    return r;
}

#endif /* ndef NOPERL */

/* Validation without Perl structures. */

static void
c_validate (parser_t * parser)
{
    /* The currently-parsed character. */	
						
    char c;					

    /* If the string is empty, throw an exception. */

    if (parser->length == 0) {
	parser->bad_type = json_initial_state;
	parser->error = json_error_empty_input;
	failbadinput (parser);
    }

    SETUPPARSER;

 validate_start:

    switch (NEXTBYTE) {

    case '{':
	valid_object (parser);
	break;

    case '[':
	valid_array (parser);
	break;

    case '-':
    case '0':
    case DIGIT19:
	parser->top_level_value = 1;
	valid_number (parser);
	break;

    case '"':
	parser->top_level_value = 1;
	valid_string (parser);
	break;

    case 't':
	parser->top_level_value = 1;
	valid_literal_true (parser);
	break;

    case 'f':
	parser->top_level_value = 1;
	valid_literal_false (parser);
	break;

    case 'n':
	parser->top_level_value = 1;
	valid_literal_null (parser);
	break;

    case WHITESPACE:
	goto validate_start;

    default:
	BADCHAR;
    }

    check_end (parser);
}

static INLINE void
print_tokens (json_token_t * t)
{
    printf ("Start: %d End: %d: Type: %s\n", t->start, t->end,
	    token_names[t->type]);
    if (t->child) {
	printf ("Children:\n");
	print_tokens (t->child);
    }
    if (t->next) {
	printf ("Next:\n");
	print_tokens (t->next);
    }
}

#ifndef NOPERL

static json_token_t *
c_tokenize (parser_t * parser)
{
    /* The currently-parsed character. */	
						
    char c;					
    json_token_t * r;

    SETUPPARSER;

 tokenize_start:

    switch (NEXTBYTE) {

    case '{':
	r = tokenize_object (parser);
	break;

    case '[':
	r = tokenize_array (parser);
	break;

    case WHITESPACE:
	goto tokenize_start;

    default:
	BADCHAR;
    }

    check_end (parser);
    /*
    printf ("TOKENS:\n");
    print_tokens (r);
    */
    return r;
}

static void
tokenize_free (json_token_t * token)
{
    json_token_t * next;
    //    printf ("Freeing tokens.\n");
    next = token->child;
    if (next) {
	tokenize_free (next);
    }
    next = token->next;
    if (next) {
	tokenize_free (next);
    }
    Safefree (token);
}

/* This is the entry point for validation. */

static void
validate (SV * json)
{
    ENTRYDECL;

    GETSTRING;

    c_validate (& parser_o);
}

static json_token_t *
tokenize (SV * json)
{
    ENTRYDECL;

    GETSTRING;

    return c_tokenize (& parser_o);
}

#endif /* ndef NOPERL */

