/* JSON65 - A JSON parser for the 6502 microprocessor. https://github.com/ppelleti/json65 Copyright © 2018 Patrick Pelletier This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef J65_H #define J65_H #include #include /* for size_t */ /* An event which is passed to the callback function, indicating what has happened in the parser. Most events have no associated data, but a few have a string and/or an integer, as noted below, which may be retrieved with j65_get_string() or j65_get_integer(). */ enum j65_event { J65_NULL = 0, J65_FALSE = 1, J65_TRUE = 2, J65_INTEGER = 3, /* integer and string */ J65_NUMBER = 4, /* string */ J65_STRING = 5, /* string */ J65_KEY = 6, /* string */ J65_START_OBJ = 7, J65_END_OBJ = 8, J65_START_ARRAY = 9, J65_END_ARRAY = 10, }; /* A status value returned by j65_parse(). J65_DONE indicates that a complete JSON value has been parsed successfully. J65_WANT_MORE indicates that j65_parse() should be called again with more input. (If the end of the file has been reached, this can be considered an "unexpected end of file" error.) Negative values indicate errors. The error may be one of the predefined errors below, or it may be an error returned by the callback. User-defined error numbers should be between J65_USER_ERROR and -1, inclusive. If you want to define your own error codes, I recommend doing it something like this: enum { MYERR_MISSING_REQUIRED_KEY = J65_USER_ERROR, MYERR_UNSUPPORTED_KEY, MYERR_VALUE_OUT_OF_RANGE, MYERR_SOME_OTHER_ERROR, }; This way, your error codes will begin at J65_USER_ERROR, and grow upwards (towards 0). */ enum j65_status { J65_DONE = 1, J65_WANT_MORE = 2, /* errors */ J65_PARSE_ERROR = -128, J65_ILLEGAL_CHAR, J65_ILLEGAL_ESCAPE, J65_NESTING_TOO_DEEP, J65_STRING_TOO_LONG, J65_EXPECTED_STRING, J65_EXPECTED_COLON, J65_EXPECTED_COMMA, J65_EXPECTED_OBJ_END, J65_EXPECTED_ARRAY_END, J65_USER_ERROR, /* must be last. not generated by parser. */ }; /* The state for a JSON parser. Initialize it by calling j65_init(). It is too big to be allocated on the cc65 stack, so you should either allocate it statically, or on the heap. */ typedef struct { uint8_t dummy[512]; } j65_parser; /* The type of the callback function passed to j65_init. This is called from within j65_parse() whenever a parsing "event" occurs. The event type (the j65_event enumeration, but passed as a uint8_t) indicates which event occurred. Depending on the event type, additional information may be available by calling one of the accessor functions on the j65_parser. (See functions below.) If the event was processed successfully, the callback should return zero. If the callback wishes to terminate the parsing early, and cause j65_parse() to return immediately, the callback should return a negative value. (Specifically, it should return an integer between J65_USER_ERROR and -1, inclusive.) */ typedef int8_t __fastcall__ (*j65_callback)(j65_parser *p, uint8_t event); /* Initializes a j65_parser structure. The arguments passed will be stored in the j65_parser structure. Specifically: ctx is a pointer with no predefined meaning. It may be retrieved by calling j65_get_context() on the parser. cb is the callback which should be called by j65_parse() when an event occurs. max_depth is the maximum depth of nested objects and arrays allowed when parsing the JSON. max_depth will automatically be capped at 224. It may sometimes be helpful to limit max_depth to a smaller value. (For example, if you are going to build up a tree and then walk it recursively, the 6502 stack cannot hold 224 return addresses, so you could limit max_depth to a value somewhat less than 128, to prevent overflowing the stack.) To obtain the value actually used for max_depth, call j65_get_max_depth() on the parser. If max_depth is 0, then it will be set to the maximum allowable value, which is 224. So, if you do not wish to further limit the maximum depth, pass 0 for max_depth. This means that the smallest value you can actually set max_depth to is 1, which means that you can only have a top-level array or object, but no arrays or objects inside of it. (Hypothetically, a max_depth of 0 would mean only top-level scalars are accepted, and no arrays or objects would be allowed at all. But we do not let you set max_depth to 0.) Once the j65_parser has been initialized, you may call j65_parse() on it. */ void __fastcall__ j65_init (j65_parser *p, void *ctx, j65_callback cb, uint8_t max_depth); /* Parses the JSON in buf, of length len. j65_parse() may be called multiple times (as long as it returns J65_WANT_MORE) to parse input incrementally. j65_parse() calls the callback (supplied to j65_init()) as events occur. The return value is the j65_status enumeration, returned as an int8_t. J65_DONE indicates the parsing completed successfully. J65_WANT_MORE indicates that j65_parse() should be called again with more input. Any negative value indicates an error, either one generated by the parser, or one returned by the callback. */ int8_t __fastcall__ j65_parse (j65_parser *p, const char *buf, size_t len); /* Returns the string associated with the current event. This call is only valid inside the callback function, and only when the event is one of J65_INTEGER, J65_NUMBER, J65_STRING, or J65_KEY. The string returned is only valid until the callback returns. The string is NUL-terminated, so it is not necessary to call j65_get_length(), unless you wish to support strings with embedded NUL characters. In the case of a J65_NUMBER OR J65_KEY event, backslash escape sequences in the string have already been substituted. The string is UTF-8 encoded. In the case of a J65_NUMBER event, beware that although the number has been validated to contain only characters that are legal in a number, the number has not been fully validated to conform to the format for a legal number (specified in section 6 of RFC 8259). For example, the string might be "10.10.10-e++", which is not a valid number. So, you will need to more fully validate the number when parsing it, and return a user error from the callback if necessary. */ const char * __fastcall__ j65_get_string (const j65_parser *p); /* In the cases where j65_get_string() is valid, j65_get_length() is valid, too. It returns the length of the string returned by j65_get_string(). This is necessary if you wish to support strings with embedded NUL characters, and it may also be useful in other situations, such as if you want to avoid the performance hit of calling strlen() on the string returned by j65_get_string(). */ uint8_t __fastcall__ j65_get_length (const j65_parser *p); /* Returns the integer associated with the current event. This call is only valid inside the callback function, and only when the event is J65_INTEGER. Note that in a J65_INTEGER event, both j65_get_integer() and j65_get_string() are valid, so you may process the integer either as an integer or a string. If a number cannot be represented as an int32_t, either because it is non-integral, or because it is too large, then a J65_NUMBER event is generated instead of J65_INTEGER. */ int32_t __fastcall__ j65_get_integer (const j65_parser *p); /* Returns the offset within the file of the beginning of the current line. (In other words, the total number of bytes processed by this j65_parser, prior to the first byte of the current line.) j65_get_line_offset() may be called either within the callback function (for any event type), or after j65_parse() returns. This can be useful if you want to print the current line when an error occurs. */ uint32_t __fastcall__ j65_get_line_offset (const j65_parser *p); /* Returns the line number of the current line. j65_get_line_number() may be called either within the callback function (for any event type), or after j65_parse() returns. The line number is 0-based, so you will probably want to add 1 before displaying it to the user. Lines may be terminated either by a linefeed (UNIX standard) or a carriage return (Apple II standard). However, a linefeed is not counted if it is immediately preceded by a carriage return. (Thus allowing MS-DOS/Windows standard line endings to be supported as well.) */ uint32_t __fastcall__ j65_get_line_number (const j65_parser *p); /* Like j65_get_line_number(), but returns the current column number within the current line. Like j65_get_line_number(), the column number is 0-based, and may be obtained at any time, either during or after parsing. When inside the callback, the column number usually indicates the last byte of the value which generated the current event, or the byte immediately after it. When j65_parse() returns with an error, the column may either point to the byte where the error occurred, or the last byte of the value which generated the error, or the byte immediately following it. The column number simply counts bytes, so the number may be unintuitive if your input string contains tabs or multibyte UTF-8 characters. */ uint32_t __fastcall__ j65_get_column_number (const j65_parser *p); /* Returns the current depth of nested arrays and objects. It can be between 0 and max_depth (which is never more than 224), inclusive. Depth 0 only occurs for top-level scalars. */ uint8_t __fastcall__ j65_get_current_depth (const j65_parser *p); /* Returns the maximum value that j65_get_current_depth() can have. This is the value of max_depth which was supplied to j65_init(), unless the value supplied was 0 or was greater than 224, in which case the max depth will be 224. If this depth is exceeded, the parser will return the error code J65_NESTING_TOO_DEEP. */ uint8_t __fastcall__ j65_get_max_depth (const j65_parser *p); /* Returns the ctx pointer which was supplied to j65_init(). This can be used for passing arbitrary data to the callback. */ void * __fastcall__ j65_get_context (const j65_parser *p); #endif /* J65_H */