Showing preview only (208K chars total). Download the full file or copy to clipboard to get everything.
Repository: ppelleti/json65
Branch: master
Commit: 878d295f1d51
Files: 43
Total size: 196.7 KB
Directory structure:
gitextract_3i7gmx_0/
├── .gitignore
├── LICENSE.txt
├── README.md
├── examples/
│ ├── example.c
│ └── input.json
├── run-tests.pl
├── src/
│ ├── json65-file.c
│ ├── json65-file.h
│ ├── json65-print.c
│ ├── json65-print.h
│ ├── json65-quote.h
│ ├── json65-quote.s
│ ├── json65-string.h
│ ├── json65-string.s
│ ├── json65-tree.c
│ ├── json65-tree.h
│ ├── json65.h
│ └── json65.s
├── tests/
│ ├── create-testfile-disk-image.pl
│ ├── file00.json
│ ├── file01.json
│ ├── file02.json
│ ├── file03.json
│ ├── file04.json
│ ├── file05.json
│ ├── file06.json
│ ├── file07.json
│ ├── test-file.c
│ ├── test-print.c
│ ├── test-print.json
│ ├── test-quote.c
│ ├── test-string.c
│ ├── test-tree.c
│ ├── test-tree.json
│ └── test.c
└── tools/
├── README.md
├── asm-heading.hs
├── check-endproc.pl
├── convert_enums.pl
├── debug.inc
├── make_charprops.pl
├── make_dispatch.pl
└── random-json.hs
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*~
*.o
*.map
*.tmp
test
test-string
test-tree
test-quote
test-print
testfile.system
testfile.po
example.system
================================================
FILE: LICENSE.txt
================================================
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.
================================================
FILE: README.md
================================================
I was watching TV, and there was a commercial which proclaimed, "It's
time to do what *you* want!" I replied to the TV, "It's time to write
a JSON parser in 6502 assembly language?" Somehow I don't think
that's what they had in mind, but the TV is right, I *should* do what
I want.
So, here is my JSON parser. The core parser is written entirely in
6502 assembly language, and is meant to be assembled with [ca65][1].
However, it is meant to be called from C, and uses the
[cc65 calling convention][2] (specifically, the `fastcall` convention).
JSON65 should work on any processor in the 6502 family. (It does not
use any 65C02 instructions.)
The assembly language parts of JSON65 use the zero page locations used
by `cc65`, in a way which is compatible with the C calling convention.
JSON65 should work on any target supported by the `cc65` toolchain. I
have tested it on `sim65` and on an unenhanced Apple //e.
## Parser (json65.h)
JSON65 is an event-driven (SAX-style) parser, so the parser is given a
callback function, which it calls for each event.
JSON65 supports incremental parsing, so you can freely feed it any
sized chunks of input, and you don't need to have the whole file in
memory at once.
JSON65 is fully reentrant, so you can incrementally parse several
files at once if you so desire.
JSON65 does have a couple of limits: strings are limited to 255 bytes,
and the nesting depth (of nested arrays or objects) is limited to 224.
However, there is no limit on the length of a line, or the length of a
file.
JSON65 uses 512 bytes of memory for each parser, which must be
allocated by the caller. JSON65 does not use dynamic memory
allocation.
In accordance with the [JSON specification][3], JSON65 assumes its
input is UTF-8 encoded. However JSON65 does not validate the UTF-8,
so any encoding can be used, as long as all bytes with the high bit
clear represent ASCII characters. Bytes with the high bit set are
only allowed inside strings. The only place where JSON65 assumes
UTF-8 is in the processing of `\u` escape sequences. In accordance
with the JSON specification, a single `\u` escape can be used to
specify code points in the Basic Multilingual Plane, and two
consecutive `\u` escapes (a UTF-16 surrogate pair) can be used to
specify a code point outside the Basic Multilingual Plane. These
escapes will be translated into the proper UTF-8.
Because JSON only allows newlines in places where arbitrary whitespace
is allowed, JSON65 is agnostic to the type of line ending. (CR, LF,
or CRLF.) For the purposes of counting line numbers for error
reporting, JSON65 handles CR, LF, or CRLF line endings.
JSON65 will parse numbers which fit into a 32-bit signed long, and
will provide the long to the callback. All other numbers
(i. e. floating point numbers, or integers which overflow a 32-bit
long) are provided to the callback as a string. (Like strings,
numbers cannot be more than 255 digits long.)
The callback function may return an error if it wishes. This will
cause parsing to stop immediately, and the error code returned by the
callback will be returned by `j65_parse()`. Error codes are negative
numbers, and the user may use the codes from `J65_USER_ERROR` to
`-1`, inclusive, for their own error codes.
## Tree interface (json65-tree.h)
If you use the event-driver parser, you'll need to build your own data
structure (or otherwise handle the data somehow) as the events come
in. If you don't want to do that, you can use the tree interface
(`json65-tree.h`) instead, which builds up a data structure for you.
This only works for small files, because the entire tree has to fit in
memory at once.
Unlike the event-based parser, the tree interface uses dynamic memory
allocation.
## Printing JSON (json65-print.h)
Mostly, JSON65 is a parser. However, it does have some support for
printing JSON back to a file, in `json65-print.h`. The function
`j65_print_tree()` will print a JSON tree (from the tree interface in
`json65-tree.h`) to a given filehandle. It prints the entire JSON
tree on a single line with no whitespace. This is the most compact
format for a machine-readable JSON file, but it is not particularly
human-readable.
If you write your own code to print JSON, either because you want to
pretty-print it, or because you are using a data structure other than
`j65_node`, you may still want to use the function
`j65_print_escaped()` from `json65-quote.h`. It handles escaping a
string using the JSON escape sequences.
## API documentation
I don't have any fancy Doxygen documentation, but the API is
documented by comments in the header files. If you wish to use the
event-driven parser, read [json65.h](src/json65.h). If you wish to
use the tree interface, read [json65-tree.h](src/json65-tree.h).
## Library organization
If you simply wish to use the event-driven (SAX-style) parser, you
only need one header file (`json65.h`) and one assembly file
(`json65.s`). However, there are some helper functions in other
files, which you can optionally use with JSON65 if you like. Most
notable is the tree interface to JSON65, which you may use instead of
the event-driven interface for small files.
Each header file corresponds directly to one implementation file.
Some of the implementation files are written in assembly language, and
some are written in C. Here is a description of each, along with the
size of the machine code of the implementation (`CODE` section plus
`RODATA` section; none of the implementation files have any `DATA` or
`BSS`).
* [json65.h](src/json65.h) (2240 bytes) - The core, event-driven
parser. This is the only file that is required if you wish to build
your own data structure.
* [json65-string.h](src/json65-string.h) (291 bytes) - This implements
a [string intern pool][4] which is used by the tree interface.
* [json65-tree.h](src/json65-tree.h) (1300 bytes) - The tree
interface, which builds up a tree data structure as the file is
parsed. You may then traverse the tree to your heart's content.
* [json65-quote.h](src/json65-quote.h) (226 bytes) - This has a
function which prints strings, replacing special characters with the
escape sequences from the JSON specification. It is used by the
tree printer, but can also be used standalone if you are printing
JSON files yourself without using the tree interface.
* [json65-print.h](src/json65-print.h) (710 bytes) - Prints a tree to
a file as JSON. Use this if you are using the tree interface, and
wish to write JSON files as well as read them.
* [json65-file.h](src/json65-file.h) (1378 bytes) - Provides a helper
function to feed data to the parser from a file, in chunks, and to
display error messages to the user (including printing the offending
line, and printing a caret to indicate the offending position of the
line).
I hate build systems (or at least, build systems for C code), so I
have not provided one. (Other than a lame little Perl script to build
and run the tests using [sim65][5].) Instead, I encourage you to copy
the source files and header files you need into your own project, and
use whatever build system you are already using for your project.
(Such as the GNU Make based [cc65 build system][6].)
You can use the following dependency graph to determine which source
files you will need to copy into your project. (For each source file,
you will also need to copy the corresponding header file.) Source
files with no dependencies (such as `json65.s`) are at the top of the
graph, while the source file with the most dependencies
(`json65-print.c`) is at the bottom of the graph.
```
json65.s json65-string.s
/ \ /
/ \ /
/ \ /
json65-file.c json65-tree.c json65-quote.s
\ /
\ /
\ /
json65-print.c
```
If you wish to build and run the tests, simply run the `run-test.pl`
Perl script at the top level of the repository. (It takes no
arguments.) You'll need to have the [cc65][7] toolchain installed.
Note: version 2.17 and earlier of [sim65][5] have a
[bug in the implementation of the BIT instruction][8], so the tests
will fail. You'll need a more recent version to get the tests to
pass. (This only affects the simulation of the tests. If you plan on
running JSON65 on real hardware, or on an emulator *other* than sim65,
then you'll be fine with an older version of cc65.)
## License
JSON65 is licensed under the [zlib/libpng license](LICENSE.txt), which
is approved by the [OSI][9] and the [FSF][10].
## Background
For more information about how and why I wrote JSON65, see [my blog post][11].
[1]: https://cc65.github.io/doc/ca65.html
[2]: https://cc65.github.io/doc/cc65-intern.html
[3]: https://tools.ietf.org/html/rfc8259#section-8.1
[4]: https://en.wikipedia.org/wiki/String_interning
[5]: https://cc65.github.io/doc/sim65.html
[6]: https://github.com/cc65/wiki/wiki/Bigger-Projects
[7]: https://cc65.github.io/cc65/
[8]: https://github.com/cc65/cc65/pull/712
[9]: https://opensource.org/licenses/Zlib
[10]: https://www.gnu.org/licenses/license-list.en.html#ZLib
[11]: https://funwithsoftware.org/posts/2021-03-03-json-on-the-apple-ii.html
================================================
FILE: examples/example.c
================================================
/*
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.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <cc65.h>
#include "json65.h"
#include "json65-file.h"
#include "json65-tree.h"
#include "json65-print.h"
static uint8_t scratch[2048];
static j65_tree tree;
static void my_exit (int code) __attribute__ ((noreturn)) {
if (doesclrscrafterexit ()) {
cursor (true);
cgetc ();
}
exit (code);
}
int main (int argc, char **argv) {
const char *filename;
FILE *f;
int8_t ret;
j65_node *banana, *banana_value;
uint8_t width, height;
if (argc >= 2) {
filename = argv[1];
} else {
filename = "input.json";
}
/* Find out the width of the screen. (This will be used
** when formatting error messages.)
*/
screensize (&width, &height);
/* Initialize the tree data structure. */
j65_init_tree (&tree);
/* Open the input file. */
f = fopen (filename, "r");
if (!f) {
perror (filename);
my_exit (EXIT_FAILURE);
}
/* Call j65_parse_file() to parse the file, and it will in turn
** call j65_tree_callback() to build up the tree.
*/
ret = j65_parse_file (f, /* file to parse */
scratch, /* pointer to a scratch buffer */
sizeof (scratch), /* length of scratch buffer */
&tree, /* "context" for callback */
j65_tree_callback, /* the callback function */
0, /* 0 means use max nesting depth */
stderr, /* where to print errors */
width, /* width of screen (for errors) */
filename, /* used in error messages */
NULL); /* no custom error func */
if (ret < 0) {
/* Don't need to print any error message here, because
** j65_parse_file() already printed an error message before
** returning a negative number.
*/
fclose (f);
my_exit (EXIT_FAILURE);
}
/* We're done reading the file, so we can close it now. */
fclose (f);
/* Look for a key named "banana". */
banana = j65_find_key (&tree, tree.root, "banana");
if (banana == NULL) {
printf ("Could not find banana.\n");
j65_free_tree (&tree);
my_exit (EXIT_FAILURE);
}
/* The variable "banana" now points to the key "banana" in
** the tree. If we want to know the value associated with
** the key "banana", we need to look at the key's child node.
*/
banana_value = banana->child;
/* Now print the value associated with the key "banana". */
printf ("Here are some fun facts about bananas, from line %lu of %s:\n",
banana_value->location.line_number + 1, filename);
ret = j65_print_tree (banana_value, stdout);
if (ret < 0) {
perror ("error printing tree");
j65_free_tree (&tree);
my_exit (EXIT_FAILURE);
}
/* j65_print_tree() prints everything on one line (so, not
** particularly human readable) without a newline at the
** end, so we must print the newline ourselves.
*/
printf ("\n");
/* We are done, so we can free the tree now. */
j65_free_tree (&tree);
my_exit (EXIT_SUCCESS);
}
================================================
FILE: examples/input.json
================================================
{
"apple": 2,
"banana": {
"color": "yellow",
"edible": true,
"sieverts": 9.82e-8
},
"raspberry": 3.14519
}
================================================
FILE: run-tests.pl
================================================
#!/usr/bin/perl -w
# 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.
use FindBin;
my $root = $FindBin::Bin;
chdir ($root);
my $src = "src";
my $test = "tests";
my $example = "examples";
my $blue = "\e[34m";
my $green = "\e[32m";
my $red = "\e[31m";
my $off = "\e[0m";
my %test_results = ();
my $total_bytes = 0;
sub mysystem {
my @cmd = @_;
print join(" ", @cmd), "\n";
if (system (@cmd) != 0) {
if ($? == -1) {
die "$red*** fatal: $!$off\n";
} elsif ($? & 127) {
die (sprintf ("$red*** fatal: signal %d$off\n", $? & 127));
} else {
die (sprintf ("$red*** fatal: exit code %d$off\n", $? >> 8));
}
}
}
sub mysystem_nonfatal {
my @cmd = @_;
print join(" ", @cmd), "\n";
if (system (@cmd) != 0) {
if ($? == -1) {
die "$red*** fatal: $!$off\n";
} elsif ($? & 127) {
die (sprintf ("$red*** fatal: signal %d$off\n", $? & 127));
} else {
return "fail";
}
}
return "pass";
}
sub print_heading {
my $str = $_[0];
print "$blue*** $str$off\n";
}
sub build_program {
my ($hash, @sources) = @_;
my $prog = $hash->{"prog"};
my $map = $prog . ".map";
my $target = "sim6502";
$target = $hash->{"target"} if (exists $hash->{"target"});
my @cmd = ("cl65", "-I$src", "-W-unused-param", "-O");
push @cmd, '-t', $target;
push @cmd, '-C', $hash->{"config"} if (exists $hash->{"config"});
push @cmd, '-o', $prog;
push @cmd, '-m', $map;
push @cmd, @sources;
mysystem (@cmd);
}
sub run_test {
my $t = $_[0];
print_heading "Running $t";
$test_results{$t} = mysystem_nonfatal ("sim65", $t);
}
sub parse_map {
my $map = $_[0];
my $name = "";
my $size = 0;
my %result = ();
open F, $map or die;
while (<F>) {
chomp;
if (/^([^:\s]+):$/) {
$name = $1;
$size = 0;
} elsif (/^\s+[A-Z].* Size\=(\w+)/) {
my $sz = hex ($1);
$size += $sz;
$result{$name} = $size;
} elsif (/^$/) {
last;
}
}
close F;
return \%result;
}
sub print_size {
my ($sizes, $obj) = @_;
my $size = $sizes->{$obj};
printf "%-15s %4u bytes\n", $obj, $size;
$total_bytes += $size;
}
print_heading "Building tests";
# Tests which can be built for sim65
build_program({'prog' => "$test/test"},
"$src/json65.s", "$test/test.c");
build_program({'prog' => "$test/test-string"},
"$src/json65-string.s", "$test/test-string.c");
build_program({'prog' => "$test/test-tree"},
"$src/json65.s", "$src/json65-string.s", "$src/json65-tree.c",
"$test/test-tree.c");
build_program({'prog' => "$test/test-quote"},
"$src/json65-quote.s", "$test/test-quote.c");
build_program({'prog' => "$test/test-print"},
"$src/json65.s", "$src/json65-string.s", "$src/json65-tree.c",
"$src/json65-quote.s", "$src/json65-print.c",
"$test/test-print.c");
# test-file uses library functions (ftell and fseek) which are not available
# on sim65, so we build it for Apple II instead. This means that we cannot
# test it automatically, though. (But it's still worth building, to make
# sure it builds.)
build_program({'prog' => "$test/testfile.system",
'target' => 'apple2',
'config' => 'apple2-system.cfg'},
"$src/json65.s", "$src/json65-file.c", "$test/test-file.c");
build_program({'prog' => "$example/example.system",
'target' => 'apple2',
'config' => 'apple2-system.cfg'},
"$src/json65.s", "$src/json65-file.c",
"$src/json65-string.s", "$src/json65-tree.c",
"$src/json65-quote.s", "$src/json65-print.c",
"$example/example.c");
chdir ($test);
# Run tests on sim65
run_test ("test");
run_test ("test-string");
run_test ("test-tree");
run_test ("test-print");
# test-quote is not self-checking, and its functionality is subsumed
# by test-print, so there's no need to run it
#run_test ("test-quote");
print_heading "Size summary";
my $print_map = parse_map ("test-print.map");
my $file_map = parse_map ("testfile.system.map");
print_size ($print_map, "json65.o");
print_size ($print_map, "json65-string.o");
print_size ($print_map, "json65-tree.o");
print_size ($print_map, "json65-quote.o");
print_size ($print_map, "json65-print.o");
print_size ($file_map, "json65-file.o");
printf "%-15s %4u bytes\n", "total", $total_bytes;
my $failures = 0;
print_heading "Test summary";
foreach my $t (sort keys %test_results) {
printf "%-11s ", $t;
if ($test_results{$t} eq "pass") {
print $green, "PASS", $off, "\n";
} else {
print $red, "FAIL", $off, "\n";
$failures++;
}
}
exit $failures;
================================================
FILE: src/json65-file.c
================================================
/*
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.
*/
#include <errno.h>
#include <string.h>
#include "json65-file.h"
static const char insufficient_memory[] = "insufficient memory";
static const char *get_errmsg (int8_t n) {
switch (n) {
case J65_PARSE_ERROR: return "parse error";
case J65_ILLEGAL_CHAR: return "illegal character";
case J65_ILLEGAL_ESCAPE: return "illegal escape sequence";
case J65_STRING_TOO_LONG: return "string longer than 255 bytes";
case J65_EXPECTED_STRING: return "keys must be strings";
case J65_EXPECTED_COLON: return "expected ':'";
case J65_EXPECTED_COMMA: return "expected ','";
case J65_EXPECTED_OBJ_END: return "expected '}'";
case J65_EXPECTED_ARRAY_END: return "expected ']'";
default: return NULL;
}
}
int8_t __fastcall__ j65_parse_file (FILE *f,
void *scratch,
size_t scratch_len,
void *ctx,
j65_callback cb,
uint8_t max_depth,
FILE *err,
uint8_t width,
const char *filename,
j65_err_func user_err_func) {
j65_parser *p;
char *buf;
size_t buflen;
long origin;
int8_t ret;
size_t size;
uint32_t line_num, column_num;
const char *errmsg;
uint32_t offset;
if (scratch_len < sizeof (j65_parser) + width + 1) {
fprintf (err, "%s %u\n", insufficient_memory, scratch_len);
return J65_INSUFFICIENT_MEMORY;
}
p = (j65_parser *) scratch;
buf = ((char *) scratch) + sizeof (j65_parser);
buflen = scratch_len - sizeof (j65_parser);
j65_init (p, ctx, cb, max_depth);
/* I am having a weird problem with ftell returning 512 when
** I am at the beginning of the file. Until I can figure that
** out, just assume that f is at the beginning of the file.
**
** The bug report is here:
** https://github.com/cc65/cc65/issues/722
*/
#if 1
origin = 0;
#else
origin = ftell (f);
if (origin < 0)
goto io_error;
#endif
do {
size = fread (buf, 1, buflen, f);
if (ferror (f))
goto io_error;
ret = j65_parse (p, buf, size);
} while (ret == J65_WANT_MORE && !feof (f));
if (ret == J65_WANT_MORE) {
fprintf (err, "%s: Unexpected end of file\n", filename);
return J65_UNEXPECTED_END_OF_FILE;
}
if (ret == J65_DONE) {
return ret;
}
line_num = j65_get_line_number (p);
column_num = j65_get_column_number (p);
fprintf (err, "%s:%lu:%lu: ", filename, line_num + 1, column_num + 1);
errmsg = get_errmsg (ret);
if (errmsg != NULL) {
fputs (errmsg, err);
} else if (ret == J65_NESTING_TOO_DEEP) {
fprintf (err, "exceeded max nesting depth of %u levels",
j65_get_max_depth (p));
} else {
if (user_err_func == NULL)
user_err_func = j65_default_err_func;
user_err_func (err, ctx, ret);
}
fputc ('\n', err);
width--;
offset = j65_get_line_offset (p);
if (column_num >= width) {
offset += (column_num - (width - 1));
column_num = width - 1;
}
if (fseek (f, origin + offset, SEEK_SET) < 0)
goto io_error;
size = fread (buf, 1, width, f);
buf[size] = 0;
size = strcspn (buf, "\r\n");
buf[size] = 0;
fprintf (err, "%s\n", buf);
memset (buf, ' ', column_num);
buf[column_num] = '^';
buf[column_num + 1] = 0;
fprintf (err, "%s\n", buf);
return ret;
io_error:
if (_oserror)
errmsg = _stroserror (_oserror);
else
errmsg = strerror (errno);
fprintf (err, "%s: %s\n", filename, errmsg);
return J65_IO_ERROR;
}
void __fastcall__ j65_default_err_func (FILE *err,
void *ctx,
int8_t status) {
if (status == J65_INSUFFICIENT_MEMORY) {
fputs (insufficient_memory, err);
} else {
fprintf (err, "Unknown error code %d", status);
}
}
================================================
FILE: src/json65-file.h
================================================
/*
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_FILE_H
#define J65_FILE_H
#include <stdint.h>
#include <stdio.h>
#include "json65.h"
/*
These are additional error codes that can be returned, besides
the ones in j65_status.
*/
enum {
J65_INSUFFICIENT_MEMORY = -1, /* scratch_len was too small */
J65_IO_ERROR = -2, /* file I/O returned an error */
J65_UNEXPECTED_END_OF_FILE = -3, /* incomplete JSON value */
};
/*
This optional callback function should translate your custom
error code (in the status argument) to a human-readable
string, and print it on the err filehandle. The ctx argument
is supplied in case you want your error message to contain
additional information from the context.
If you do not recognize the error code, you can pass it along
to j65_default_err_func().
*/
typedef void __fastcall__ (*j65_err_func) (FILE *err,
void *ctx,
int8_t status);
/*
Parses JSON from a file. If an error occurs, prints a nice
human-readable error message, including the line number and
column number, and prints the offending line with a caret
pointing to the offending position.
Does not do any dynamic memory allocation. However, it requires a
chunk of scratch memory. The scratch memory must be at least 513 +
width bytes, and I would recommend at least 768 bytes, though 1K or
2K might be preferable, from a performance standpoint. (But,
experiment and see what works best on your operating system.)
The scratch buffer does not need to be big enough to hold the whole
file, though.
Returns J65_DONE if the file is parsed successfully. If an error
occurs, returns a negative number which will either be one of the
error codes from j65_status in json65.h, or one of the error codes
in the enumation at the top of this file. In the case of an error,
a human-readable message will have been printed to the err
filehandle (so you may not need to care which negative value was
returned, just that it was negative). Never returns J65_WANT_MORE.
(Feeding chunks of the file is handled automatically, and if
J65_WANT_MORE occurs at the end of the file, that is translated
into J65_UNEXPECTED_END_OF_FILE.)
Here are the arguments:
f - The file to parse.
scratch - Pointer to scratch memory.
scratch_len - Size of scratch memory, in bytes. If it is
not at least 513 + width bytes, the function returns
J65_INSUFFICIENT_MEMORY.
ctx - Context argument which will be passed to callback. (To pass
a pointer to any data structure you desire.)
cb - The callback which is called for each event in the JSON file.
For more information, see documentation in json65.h.
max_depth - The maximum nesting depth (of nested arrays and objects)
allowed. If you do not care, set it to 0. See documentation for
j65_init() in json.h for additional information.
err - The file handle (such as stdout, stderr, or an actual file)
to print the error message to, if an error occurs.
width - The width, in characters, of the display device for the
err filehandle. If the err filehandle represents the screen, you
can get the width with the screensize() function from the cc65
standard header file conio.h. If err is a real file, then just
choose something reasonable, like 80.
filename - The name of the file represented by the f filehandle.
This name is only used in generating an error message; we do not
attempt to access the file by name.
user_err_func - If you return custom error codes from your
callback function, you can supply a user_err_func which
translates your custom error codes to a human-readable string.
Passing NULL is the same as passing j65_default_err_func.
*/
int8_t __fastcall__ j65_parse_file (FILE *f,
void *scratch,
size_t scratch_len,
void *ctx,
j65_callback cb,
uint8_t max_depth,
FILE *err,
uint8_t width,
const char *filename,
j65_err_func user_err_func);
/*
A default implementation of j65_err_func, which prints
unknown error codes numerically.
*/
void __fastcall__ j65_default_err_func (FILE *err,
void *ctx,
int8_t status);
#endif /* J65_FILE_H */
================================================
FILE: src/json65-print.c
================================================
/*
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.
*/
#include <stdbool.h>
#include "json65-print.h"
#include "json65-quote.h"
enum {
ASCENDING,
DESCENDING,
};
/* This is a non-recursive implementation because the tree might be deep
* and the 6502 stack is not very deep. */
int __fastcall__ j65_print_tree (j65_node *root, FILE *f) {
j65_node *n = root;
uint8_t direction = DESCENDING;
bool done = false;
char c1, c2;
j65_node *next;
uint8_t next_direction;
bool print_comma;
uint8_t node_type;
if (root == NULL)
return 0;
do {
node_type = n->node_type;
/* the default (for scalars) is to visit the next sibling */
if (n->next) {
next = n->next;
next_direction = DESCENDING;
print_comma = true;
} else {
next = n->parent;
next_direction = ASCENDING;
print_comma = false;
}
switch (node_type) {
case J65_NULL:
fputs ("null", f);
break;
case J65_FALSE:
fputs ("false", f);
break;
case J65_TRUE:
fputs ("true", f);
break;
case J65_INTEGER:
fprintf (f, "%ld", n->integer);
break;
case J65_NUMBER:
fputs (n->string, f);
break;
case J65_STRING:
fputc ('\"', f);
j65_print_escaped (n->string, f);
fputc ('\"', f);
break;
case J65_KEY:
if (direction == DESCENDING) {
fputc ('\"', f);
j65_print_escaped (n->string, f);
fputs ("\":", f);
next = n->child;
next_direction = DESCENDING;
print_comma = false;
}
break;
case J65_START_OBJ:
case J65_START_ARRAY:
if (node_type == J65_START_OBJ) {
c1 = '{';
c2 = '}';
} else {
c1 = '[';
c2 = ']';
}
if (direction == DESCENDING) {
fputc (c1, f);
next = n->child;
if (next) {
next_direction = DESCENDING;
} else {
next = n;
next_direction = ASCENDING;
}
print_comma = false;
} else {
fputc (c2, f);
}
break;
default:
/* should never happen... */
fprintf (f, "?%u(%p)", node_type, n);
break;
}
if (n == root && (direction == ASCENDING ||
(node_type != J65_START_OBJ &&
node_type != J65_START_ARRAY))) {
done = true;
} else if (print_comma) {
fputc (',', f);
}
n = next;
direction = next_direction;
} while (!done && !ferror(f));
if (ferror (f))
return -1;
else
return 0;
}
================================================
FILE: src/json65-print.h
================================================
/*
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_PRINT_H
#define J65_PRINT_H
#include <stdio.h>
#include "json65-tree.h"
/*
Prints the specified tree to the specified file handle as JSON.
Returns 0 on success. If an error occurs on the file handle,
returns -1. In that case, look at errno and/or _oserror to
see what the error was.
*/
int __fastcall__ j65_print_tree (j65_node *n, FILE *f);
#endif /* J65_PRINT_H */
================================================
FILE: src/json65-quote.h
================================================
/*
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_QUOTE_H
#define J65_QUOTE_H
#include <stdio.h>
/*
Prints the specified string to the specified file handle.
Special characters are escaped using the escape sequences
from the JSON specification.
Does not return a value, so to check for an error, you
should call ferror(f).
*/
void __fastcall__ j65_print_escaped (const char *str, FILE *f);
#endif /* J65_QUOTE_H */
================================================
FILE: src/json65-quote.s
================================================
;; 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.
.macpack generic
.include "zeropage.inc"
.import _fprintf
.import _fputc
.import _fwrite
.import popax
.import pushax
.export _j65_print_escaped
fileptr = regbank
strptr = regbank + 2
startidx = regbank + 4
saveidx = regbank + 5
t1 = tmp1
len = tmp2
character = tmp3
;; pushes regbank (caller-saved registers) onto 6502 stack
.macro save_regbank
.repeat 6, i
lda regbank+i
pha
.endrep
.endmacro ; save_regbank
;; pops regbank (caller-saved registers) off of 6502 stack
.macro restore_regbank
.repeat 6, i
pla
sta regbank+5-i
.endrep
.endmacro ; restore_regbank
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_print_escaped ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; void __fastcall__ j65_print_escaped (const char *str, FILE *f)
.proc _j65_print_escaped
sta t1
save_regbank
lda t1
sta fileptr
stx fileptr+1
jsr popax
sta strptr
stx strptr+1
ldy #0
loop1: sty startidx
loop2: lda (strptr),y
cmp #'#' ; check whether character is special
bge higher
and #$fe
cmp #' '
bne special_char
okay_char: ; character does not need to be escaped
iny
jmp loop2
higher: cmp #$5c ; backslash
bne okay_char
special_char: ; character needs to be escaped
sty saveidx
tya
sub startidx
beq skip_fwrite ; skip fwrite if "count" would be 0
sta len
ldx strptr+1 ; add strptr to startidx to make "buf" argument
lda strptr
add startidx
bcc skip_inx
inx
skip_inx:
jsr pushax ; push argument "buf"
lda #1
ldx #0
jsr pushax ; push argument "size"
lda len
ldx #0
jsr pushax ; push argument "count"
lda fileptr ; argument "f" passed in ax
ldx fileptr+1
jsr _fwrite
skip_fwrite:
ldy saveidx
lda (strptr),y
beq done ; terminating NUL character
pha ; save character to be escaped
lda #$5c ; print backslash
ldx #0
jsr pushax ; push argument "c"
lda fileptr ; argument "f" passed in ax
ldx fileptr+1
jsr _fputc
pla ; restore character to be escaped
sta character
ldx #6 ; see if there is a short escape for character
esc_loop:
lda escaped_chars,x
cmp character
beq found_short_escape
dex
bpl esc_loop
lda fileptr ; there is not a short escape, use \uxxxx
ldx fileptr+1
jsr pushax ; push argument "f"
lda #<fmt_str
ldx #>fmt_str
jsr pushax ; push argument "format"
lda character
ldx #0
jsr pushax ; push varargs argument (character as int)
ldy #6 ; bytes of arguments on the stack
jsr _fprintf
continue:
ldy saveidx
iny
jmp loop1
found_short_escape:
lda escape_codes,x ; print escape code character
ldx #0
jsr pushax ; push argument "c"
lda fileptr ; argument "f" passed in ax
ldx fileptr+1
jsr _fputc
jmp continue
done: restore_regbank
rts
.rodata
escape_codes:
.byte $22, $5c, "bfnrt"
escaped_chars:
.byte $22, $5c, $08, $0c, $0a, $0d, $09
fmt_str:
.asciiz "u%04x"
.endproc ; _j65_print_escaped
================================================
FILE: src/json65-string.h
================================================
/*
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_STRING_H
#define J65_STRING_H
#include <stdint.h>
/*
j65_strings is a "string intern pool" used for "interning"
string values:
https://en.wikipedia.org/wiki/String_interning
This saves space by only keeping a single copy of each string,
and it also means interned strings can be compared by pointer
value, without having to compare character-by-character.
j65_strings is too large to fit on the stack, so it should be
allocated statically or on the heap.
To initialize the intern pool, call j65_init_strings().
Since j65_intern_string() will allocate memory with malloc()
for each unique interned string, you must call j65_free_strings()
to free that memory when you are done with the intern pool.
Internally, j65_strings is implemented as a fixed-size,
256-entry hash table, where each hash bucket is a linked list
to handle collisions.
j65_strings has an implementation limit: It only supports
strings of 255 bytes or less. If a string is longer than
255 bytes, the interned string is truncated at 255 bytes.
This should not be an issue when interning strings returned
by the JSON65 parser, since the JSON65 parser also has a
limit of 255 bytes for strings.
*/
typedef struct {
uint8_t opaque[512];
} j65_strings;
/*
Initialize a string intern pool for use. Once you have initialized
it, you may call j65_intern_string() on it.
*/
void __fastcall__ j65_init_strings (j65_strings *strs);
/*
Intern a string in the specified pool. The returned pointer
will strcmp() equal to the str argument, as long as the str
argument is 255 bytes or less in length. (If str is longer,
then the interned string will be truncated to 255 bytes.)
The returned pointer will be valid until j65_free_strings()
is called on the pool.
If the specified string does not already exist in the pool,
and malloc() returns NULL when allocating memory for the new
string, then j65_intern_string() will return NULL.
*/
const char * __fastcall__ j65_intern_string (j65_strings *strs,
const char *str);
/*
Frees all memory used by the given string pool. Once
j65_free_strings() is called, all of the pointers
returned by j65_intern_string() for this pool become
invalid.
*/
void __fastcall__ j65_free_strings (j65_strings *strs);
#endif /* J65_STRING_H */
================================================
FILE: src/json65-string.s
================================================
;; 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.
.macpack generic
.include "zeropage.inc"
.import _free
.import _malloc
.import popax
.export _j65_init_strings
.export _j65_intern_string
.export _j65_free_strings
;; take advantage of the fact that malloc and free don't
;; modify regsave or sreg
loptr = regsave
hiptr = regsave + 2
linkptr = sreg
;; they don't modify tmp1 through tmp4, either
t1 = tmp1
t2 = tmp2
hash_val = tmp3
len = tmp4
idx = tmp4
;; ptr4 is also preserved across malloc (but not free)
strptr = ptr4
;; the following doesn't need to be preserved across
;; a malloc or free
tmpptr = ptr3
;; bucket format:
;; lo byte of ptr to next bucket
;; hi byte of ptr to next bucket
;; length of string
;; 0-255 bytes of string
;; NUL byte
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_init_strings ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; void __fastcall__ j65_init_strings (j65_strings *strs);
.proc _j65_init_strings
sta ptr1
stx ptr1+1
ldy #0
tya
jsr loop
inc ptr1+1
loop: sta (ptr1),y
iny
bne loop
rts
.endproc ; _j65_init_strings
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_intern_string ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; const char *j65_intern_string (j65_strings *strs, const char *str);
.proc _j65_intern_string
sta strptr
stx strptr+1
jsr hash_str
sta hash_val
sty len
jsr popax ; get pointer to j65_strings structure
sta loptr
stx loptr+1
sta hiptr
inx
stx hiptr+1
ldy hash_val ; lookup hash bucket
lda (loptr),y
sta linkptr
lda (hiptr),y
linkloop: ; traverse linked list
sta linkptr+1
ora linkptr
beq not_found
ldy #2
lda (linkptr),y
cmp len
bne nextlink
lda linkptr ; compare string
add #3
sta tmpptr
lda linkptr+1
adc #0
sta tmpptr+1
ldy #0
strloop:
lda (strptr),y
cmp (tmpptr),y
bne nextlink
iny
cpy len
bne strloop
lda tmpptr ; return pointer to string from table
ldx tmpptr+1
fail: rts
nextlink:
ldy #0
lda (linkptr),y
tax
iny
lda (linkptr),y
stx linkptr
jmp linkloop
not_found: ; so we need to add it to the hash table
ldx #0
lda len
add #4
bcc skip
inx
skip: jsr _malloc
sta tmpptr
stx tmpptr+1
ora tmpptr+1
beq fail ; if malloc returned null, we return null
ldy hash_val
lda (hiptr),y ; add new memory to linked list
tax
lda (loptr),y
ldy #0
sta (tmpptr),y
iny
txa
sta (tmpptr),y
ldy hash_val
lda tmpptr
sta (loptr),y
lda tmpptr+1
sta (hiptr),y
ldy #2 ; store length
lda len
sta (tmpptr),y
lda tmpptr ; copy string
add #3
sta tmpptr
bcc skip2
inc tmpptr+1
skip2: ldy #0
copyloop:
cpy len
beq copydone
lda (strptr),y
sta (tmpptr),y
iny
jmp copyloop
copydone:
lda #0
sta (tmpptr),y
lda tmpptr ; return pointer to new string
ldx tmpptr+1
rts
.endproc ; _j65_intern_string
;; rotate accumulator left by 1.
.macro rotate_left
cmp #$80
rol
.endmacro ; rotate_left
;; hash the (NUL terminated) string pointed at by strptr.
;; return hash in a and length in y.
.proc hash_str
lda #0
tay
loop: sta t1
lda (strptr),y
beq done
iny
beq done0
add t1
rotate_left
rotate_left
rotate_left
jmp loop
done0: dey
done: lda t1
rts
.endproc ; hash_str
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_free_strings ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; void __fastcall__ j65_free_strings (j65_strings *strs);
.proc _j65_free_strings
sta loptr
stx loptr+1
sta hiptr
inx
stx hiptr+1
ldy #0
loop: sty idx
lda (hiptr),y
tax
lda (loptr),y
jsr freelink
ldy idx
lda #0
sta (loptr),y
sta (hiptr),y
iny
bne loop
rts
.endproc ; _j65_free_strings
;; free the chain of buckets pointed to by ax
.proc freelink
sta linkptr
stx linkptr+1
ora linkptr+1
beq done
ldy #0
lda (linkptr),y
sta t1
iny
lda (linkptr),y
sta t2
lda linkptr
ldx linkptr+1
jsr _free
lda t1
ldx t2
jmp freelink
done: rts
.endproc ; freelink
================================================
FILE: src/json65-tree.c
================================================
/*
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.
*/
#include <stdbool.h>
#include <stdlib.h> /* malloc and free */
#include "json65-tree.h"
typedef struct {
j65_strings strings;
j65_node *root;
j65_node *current;
bool add_child;
} j65_tree_internal;
void __fastcall__ j65_init_tree (j65_tree *t) {
j65_tree_internal *tree = (j65_tree_internal *) t;
j65_init_strings (&tree->strings);
tree->root = NULL;
tree->current = NULL;
tree->add_child = true;
}
int8_t __fastcall__ j65_tree_callback (j65_parser *p, uint8_t event) {
j65_tree_internal *tree = (j65_tree_internal *) j65_get_context (p);
const char *str = NULL;
j65_node *n;
switch (event) {
case J65_END_OBJ:
case J65_END_ARRAY:
if (tree->add_child) {
tree->add_child = false;
} else {
tree->current = tree->current->parent;
}
if (tree->current->parent &&
tree->current->parent->node_type == J65_KEY) {
tree->current = tree->current->parent;
}
return 0;
case J65_NUMBER:
case J65_STRING:
case J65_KEY:
str = j65_intern_string (&tree->strings, j65_get_string (p));
if (str == NULL)
return J65_OUT_OF_MEMORY;
}
n = (j65_node *) malloc (sizeof (j65_node));
if (n == NULL)
return J65_OUT_OF_MEMORY;
n->node_type = event;
n->location.line_offset = j65_get_line_offset (p);
n->location.line_number = j65_get_line_number (p);
n->location.column_number = j65_get_column_number (p);
if (tree->add_child)
n->parent = tree->current;
else
n->parent = tree->current->parent;
n->next = NULL;
n->integer = 0; /* clears all fields of union to NULL */
switch (event) {
case J65_INTEGER:
n->integer = j65_get_integer (p);
break;
case J65_KEY:
case J65_NUMBER:
case J65_STRING:
n->string = str;
break;
}
if (tree->current == NULL) {
tree->root = n;
tree->current = n;
} else if (tree->add_child) {
tree->current->child = n;
if (tree->current->node_type == J65_KEY) {
if (event == J65_START_OBJ || event == J65_START_ARRAY)
tree->current = n;
} else {
tree->current = n;
}
} else {
tree->current->next = n;
tree->current = n;
}
switch (event) {
case J65_KEY:
case J65_START_OBJ:
case J65_START_ARRAY:
tree->add_child = true;
break;
default:
tree->add_child = false;
break;
}
return 0;
}
j65_node * __fastcall__ j65_find_key (j65_tree *t,
j65_node *object,
const char *key) {
const char *k = j65_intern_string (&t->strings, key);
if (k == NULL)
return NULL;
return j65_find_interned_key (object, k);
}
j65_node * __fastcall__ j65_find_interned_key (j65_node *object,
const char *key) {
j65_node *n;
if (object->node_type == J65_START_OBJ)
n = object->child;
else if (object->node_type == J65_KEY)
n = object;
else
return NULL;
while (n != NULL) {
if (n->string == key)
return n;
n = n->next;
}
return NULL;
}
void __fastcall__ j65_free_tree (j65_tree *t) {
j65_tree_internal *tree = (j65_tree_internal *) t;
j65_node *n = tree->root;
j65_node *follow;
/* traverse the entire tree without recursion */
/* (since 6502 stack is limited) */
while (n != NULL) {
switch (n->node_type) {
case J65_KEY:
case J65_START_OBJ:
case J65_START_ARRAY:
follow = n->child;
n->child = NULL;
if (follow != NULL)
break;
/* fall thru */
default:
/* node has no children (either a leaf or empty container) */
follow = n->next;
if (follow == NULL) /* no remaining siblings, either */
follow = n->parent;
free (n);
}
n = follow;
}
tree->root = NULL;
tree->current = NULL;
tree->add_child = true;
j65_free_strings (&tree->strings);
}
================================================
FILE: src/json65-tree.h
================================================
/*
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_TREE_H
#define J65_TREE_H
#include "json65.h"
#include "json65-string.h"
/* in addition to the status codes from j65_status */
enum {
J65_OUT_OF_MEMORY = -1, /* malloc returned NULL */
};
/*
Specifies the location in the input JSON file that corresponds
to a particular node.
line_number and column_number are 0-based, so you should probably
add 1 before displaying them to the user.
line_offset is the byte position in the file where the line
specified by line_number begins. This is useful if you want
to display the line in question, because you can seek directly
to the beginning of the line in the file.
For arrays and objects, the position specified is that of the
open square bracket or open curly brace. For scalars, the
position specified is at the end of the scalar.
*/
typedef struct {
uint32_t line_offset;
uint32_t line_number;
uint32_t column_number;
} j65_source_location;
typedef struct j65_node j65_node;
/*
j65_node represents a value parsed from the JSON file.
The node_type is recycled from the j65_event enumeration
in json65.h. All event types are legal node types, with
the exception of J65_END_OBJ and J65_END_ARRAY. Since
the tree structure treats objects and arrays as containers
rather than events, they do not have a separate start and
end. Rather, J65_START_OBJ and J65_START_ARRAY are used
to specify object nodes and array nodes, respectively.
There are three types of container nodes: J65_START_ARRAY,
J65_START_OBJ, and J65_KEY. Array nodes may contain any
number of children, of any type except J65_KEY. Object
nodes may contain any number of children, but they may
only be of type J65_KEY. Key nodes must have exactly one
child, which may be of any type except J65_KEY.
Every node except the root node has a parent, which is the
J65_START_ARRAY, J65_START_OBJ, or J65_KEY node which
contains it. For the root node, the parent pointer is NULL.
Container nodes point to their first child. Each child
node points to its next sibling via the next pointer.
The final sibling has a NULL next pointer.
*/
struct j65_node {
uint8_t node_type;
j65_source_location location;
j65_node *parent;
j65_node *next;
union {
int32_t integer; /* J65_INTEGER */
struct {
const char *string; /* J65_KEY, J65_NUMBER, or J65_STRING */
j65_node *child; /* J65_KEY, J65_START_OBJ, or J65_START_ARRAY */
};
};
};
/*
This structure represents an entire tree of nodes read from
a JSON file. The j65_tree should be initialized with
j65_init_tree(), and then it can be passed as the context
argument to j65_parse(), with j65_tree_callback() as the
callback argument.
Once parsing has completed, the tree will be populated.
The root pointer points to the root (top-level) node.
All of the strings contained in the tree (in J65_STRING,
J65_NUMBER, and J65_KEY nodes) will be interned in the
strings member.
Besides the string pool and the root pointer, j65_tree also
contains a small amount of bookkeeping information used
by the callback, which is unused once parsing is complete.
*/
typedef struct {
j65_strings strings;
j65_node *root;
uint8_t internal[3];
} j65_tree;
/*
Initializes the j65_tree structure for use.
*/
void __fastcall__ j65_init_tree (j65_tree *t);
/*
This should be specified as the callback to j65_parse(),
and the j65_tree structure should be specified as the
context. This callback builds up the tree as the parser
generates events.
*/
int8_t __fastcall__ j65_tree_callback (j65_parser *p, uint8_t event);
/*
Interns the given string into the tree's intern pool, and
then searches for a J65_KEY child node of the given
J65_START_OBJ node whose key matches the given string.
The matching J65_KEY node is returned, or NULL if there is
no match.
*/
j65_node * __fastcall__ j65_find_key (j65_tree *t,
j65_node *object,
const char *key);
/*
Searches for a key node which matches the given string. The
string is expected to have already been interned in the
tree's string intern pool. The node passed in should be
of type J65_START_OBJ, and its J65_KEY children are
searched. The matching J65_KEY node is returned, or NULL if
there is no match.
*/
j65_node * __fastcall__ j65_find_interned_key (j65_node *object,
const char *key);
/*
Frees all the memory used by this tree. The tree is traversed,
and all of the nodes are freed. Additionally, j65_free_strings()
is called on the string intern pool contained within the
j65_tree structure.
*/
void __fastcall__ j65_free_tree (j65_tree *t);
#endif /* J65_TREE_H */
================================================
FILE: src/json65.h
================================================
/*
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 <stdint.h>
#include <stddef.h> /* 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 */
================================================
FILE: src/json65.s
================================================
;; 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.
.macpack generic
.include "zeropage.inc"
;; routines from the cc65 runtime library
.import callptr4
.import incsp4
.import incsp6
.import negeax
.import pushax
.import resteax
.import saveeax
.export _j65_init
.export _j65_parse
.export _j65_get_string
.export _j65_get_length
.export _j65_get_integer
.export _j65_get_line_offset
.export _j65_get_line_number
.export _j65_get_column_number
.export _j65_get_current_depth
.export _j65_get_max_depth
.export _j65_get_context
;; zero page locations
state = regbank
strbuf = regbank + 2
inbuf = regbank + 4
inbuflast = tmp4 ; length - 1
charidx = tmp3 ; position in inbuf
evtype = tmp2 ; only used as an argument to call_callback
esc_code = tmp2 ; only used as an argument to lookup_escape
tmp5 = sreg
tmp6 = sreg+1
long1 = regsave
long2 = ptr1
;; zero page locations (for _j65_parse)
jlen = ptr3
;; character properties
prop_ws = %10000000 ; must be hi bit (we use bmi/bpl to test)
prop_str = %01000000
prop_lit = %00100000
prop_int = %00010000
prop_num = %00001000
prop_sc = %00000111 ; mask for structural character field
;; j65_event
.enum
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
.endenum
;; j65_status
.enum
J65_DONE = 1
J65_WANT_MORE = 2
; errors
J65_PARSE_ERROR = $80
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.
.endenum
;; lexer state
.enum
lex_ready
lex_literal
lex_string
lex_str_escape
.endenum
;; parser state (should fit in 3 bits; otherwise need to change l_ready)
;; (order needs to match dispatch_tab and literal_errors)
.enum
par_ready
par_ready_or_close_array
par_key
par_key_or_close_object
par_need_colon
par_need_comma_or_close_array
par_need_comma_or_close_object
par_done
.endenum
;; structural characters (should fit in 3 bits)
.enum
sc_none ; an illegal character
sc_lsq ; [
sc_lcur ; {
sc_rsq ; ]
sc_rcur ; }
sc_colon ; :
sc_comma ; ,
sc_quote ; "
.endenum
;; state variables
.struct st
callback .word ; these two must be first and in this order
context .word
file_off .dword
line_off .dword
line_num .dword
col_num .dword
long_val .dword
lexer_st .byte
parser_st .byte
parser_st2 .byte
str_idx .byte
stack_idx .byte
flags .byte
stack_min .byte
prev_char .byte
.endstruct
;; loads a with specified state variable. clobbers y.
.macro getstate arg
ldy #arg
lda (state),y
.endmacro ; getstate
;; stores a to specified state variable. clobbers y.
.macro putstate arg
ldy #arg
sta (state),y
.endmacro ; putstate
;; pushes regbank (caller-saved registers) onto 6502 stack
.macro save_regbank
.repeat 6, i
lda regbank+i
pha
.endrep
.endmacro ; save_regbank
;; pops regbank (caller-saved registers) off of 6502 stack
.macro restore_regbank
.repeat 6, i
pla
sta regbank+5-i
.endrep
.endmacro ; restore_regbank
;; sign-extends a into ax. clobbers y.
.macro signextend
tay
asl
lda #0
sbc #0
eor #$ff
tax
tya
.endmacro ; signextend
.code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_init ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; void __fastcall__ j65_init(j65_state *s, void *ctx, j65_callback cb, uint8_t max_depth);
.proc _j65_init
sta tmp1 ; save max_depth
lda state ; save first 2 bytes of regbank
sta ptr1
lda state+1
sta ptr1+1
ldy #4 ; get state pointer off stack
lda (sp),y
sta state
iny
lda (sp),y
sta state+1
ldy #.sizeof(st) - 1 ; clear state variables to 0
lda #0
loop: sta (state),y
dey
bpl loop
lda #par_done
putstate st::parser_st2
lda #$ff
putstate st::stack_idx
lda #0
sub tmp1 ; subtract max depth from 256
cmp #.sizeof(st)
bge depth_ok
lda #.sizeof(st)
depth_ok:
putstate st::stack_min
ldy #3 ; copy callback and context from stack to state
loop1: lda (sp),y
sta (state),y
dey
bpl loop1
lda ptr1 ; restore first 2 bytes of regbank
sta state
lda ptr1+1
sta state+1
jmp incsp6 ; tail call to remove args from stack
.endproc ; _j65_init
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_parse ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; int8_t __fastcall__ j65_parse(j65_state *s, const char *buf, size_t len)
.proc _j65_parse
sta jlen
stx jlen+1
save_regbank ; save regbank onto 6502 stack
ldy #0 ; move state and buf from C stack to regbank
lda (sp),y
sta inbuf
iny
lda (sp),y
sta inbuf+1
iny
lda (sp),y
sta state
sta strbuf
iny
lda (sp),y
sta state+1
add #1 ; strbuf is state+256
sta strbuf+1
jsr incsp4 ; remove state and buf from C stack
loop: lda jlen+1
beq leftovers
pha ; save jlen on 6502 stack
lda jlen
pha
lda #$ff
sta inbuflast
jsr parse
tax
lda #$ff ; increment file_off by 256
jsr add_a_plus_1_to_file_off
pla ; restore jlen off 6502 stack
sta jlen
pla
sub #1 ; decrement jlen by 256
sta jlen+1
cpx #J65_WANT_MORE
bne done ; error or success; don't need to parse more
inc inbuf+1 ; add 256 to inbuf
jmp loop
leftovers: ; hi byte of jlen is zero
ldx jlen
beq done ; if length was a multiple of 256
dex
stx inbuflast
txa
pha
jsr parse
tax
pla
jsr add_a_plus_1_to_file_off
done: restore_regbank ; restore regbank off of 6502 stack
txa ; return value
signextend
rts
.endproc ; _j65_parse
;; adds a plus 1 to file_off.
;; clobbers a and y. preserves x.
.proc add_a_plus_1_to_file_off
ldy #st::file_off
sec
adc (state),y ; file_off
sta (state),y
iny
lda (state),y ; file_off+1
adc #0
sta (state),y
iny
lda (state),y ; file_off+2
adc #0
sta (state),y
iny
lda (state),y ; file_off+3
adc #0
sta (state),y
rts
.endproc ; add_a_plus_1_to_file_off
;; x will contain character, and a will contain character properties
;; on exit. clobbers y.
;; negative flag is set based on hi bit of properties. (prop_ws)
.proc getchar
ldy charidx
lda (inbuf),y
tax
bmi hiset
lda charprops,x
rts
hiset: lda #prop_str ; non-ascii unicode char, legal only in strings
rts
.rodata
charprops:
.byte 0 ; $00
.byte 0 ; $01
.byte 0 ; $02
.byte 0 ; $03
.byte 0 ; $04
.byte 0 ; $05
.byte 0 ; $06
.byte 0 ; $07
.byte 0 ; $08
.byte prop_ws ; $09
.byte prop_ws ; $0a
.byte 0 ; $0b
.byte 0 ; $0c
.byte prop_ws ; $0d
.byte 0 ; $0e
.byte 0 ; $0f
.byte 0 ; $10
.byte 0 ; $11
.byte 0 ; $12
.byte 0 ; $13
.byte 0 ; $14
.byte 0 ; $15
.byte 0 ; $16
.byte 0 ; $17
.byte 0 ; $18
.byte 0 ; $19
.byte 0 ; $1a
.byte 0 ; $1b
.byte 0 ; $1c
.byte 0 ; $1d
.byte 0 ; $1e
.byte 0 ; $1f
.byte prop_ws|prop_str ; $20
.byte prop_str ; !
.byte prop_str|sc_quote ; "
.byte prop_str ; #
.byte prop_str ; $
.byte prop_str ; %
.byte prop_str ; &
.byte prop_str ; '
.byte prop_str ; (
.byte prop_str ; )
.byte prop_str ; *
.byte prop_str|prop_num ; +
.byte prop_str|sc_comma ; ,
.byte prop_str|prop_int|prop_num ; -
.byte prop_str|prop_num ; .
.byte prop_str ; /
.byte prop_str|prop_int|prop_num ; 0
.byte prop_str|prop_int|prop_num ; 1
.byte prop_str|prop_int|prop_num ; 2
.byte prop_str|prop_int|prop_num ; 3
.byte prop_str|prop_int|prop_num ; 4
.byte prop_str|prop_int|prop_num ; 5
.byte prop_str|prop_int|prop_num ; 6
.byte prop_str|prop_int|prop_num ; 7
.byte prop_str|prop_int|prop_num ; 8
.byte prop_str|prop_int|prop_num ; 9
.byte prop_str|sc_colon ; :
.byte prop_str ; ;
.byte prop_str ; <
.byte prop_str ; =
.byte prop_str ; >
.byte prop_str ; ?
.byte prop_str ; @
.byte prop_str ; A
.byte prop_str ; B
.byte prop_str ; C
.byte prop_str ; D
.byte prop_str|prop_num ; E
.byte prop_str ; F
.byte prop_str ; G
.byte prop_str ; H
.byte prop_str ; I
.byte prop_str ; J
.byte prop_str ; K
.byte prop_str ; L
.byte prop_str ; M
.byte prop_str ; N
.byte prop_str ; O
.byte prop_str ; P
.byte prop_str ; Q
.byte prop_str ; R
.byte prop_str ; S
.byte prop_str ; T
.byte prop_str ; U
.byte prop_str ; V
.byte prop_str ; W
.byte prop_str ; X
.byte prop_str ; Y
.byte prop_str ; Z
.byte prop_str|sc_lsq ; [
.byte prop_str ; \
.byte prop_str|sc_rsq ; ]
.byte prop_str ; ^
.byte prop_str ; _
.byte prop_str ; `
.byte prop_str|prop_lit ; a
.byte prop_str ; b
.byte prop_str ; c
.byte prop_str ; d
.byte prop_str|prop_lit|prop_num ; e
.byte prop_str|prop_lit ; f
.byte prop_str ; g
.byte prop_str ; h
.byte prop_str ; i
.byte prop_str ; j
.byte prop_str ; k
.byte prop_str|prop_lit ; l
.byte prop_str ; m
.byte prop_str|prop_lit ; n
.byte prop_str ; o
.byte prop_str ; p
.byte prop_str ; q
.byte prop_str|prop_lit ; r
.byte prop_str|prop_lit ; s
.byte prop_str|prop_lit ; t
.byte prop_str|prop_lit ; u
.byte prop_str ; v
.byte prop_str ; w
.byte prop_str ; x
.byte prop_str ; y
.byte prop_str ; z
.byte prop_str|sc_lcur ; {
.byte prop_str ; |
.byte prop_str|sc_rcur ; }
.byte prop_str ; ~
.byte prop_str ; $7f
.code
.endproc ; getchar
;; state, strbuf, inbuf, and inbuflast should be set up upon entry.
;; returns status in a.
.proc parse
lda #0
sta charidx
parseloop:
getstate st::lexer_st
tay
lda lex_tab_h,y
pha
lda lex_tab_l,y
pha
rts ; jump table; not end of subroutine
l_ready:
jsr getchar
bmi got_whitespace
bit flags_prop_lit_or_num
bne start_lit
and #prop_sc
asl
asl
asl
sta tmp1
getstate st::parser_st
ora tmp1
tay
lda dispatch_tab_h,y
pha
lda dispatch_tab_l,y
pha
rts ; jump table; not end of subroutine
got_whitespace:
cpx #$0d
beq got_newline
cpx #$0a
bne jmp_nextchar
getstate st::prev_char
cmp #$0d
beq got_newline1 ; ignore LF if preceded by CR
got_newline:
ldy #st::line_num ; increment line number
jsr inc_state_long
got_newline1:
lda #0 ; set column number to 0
ldy #st::col_num
sta (state),y
iny
sta (state),y
iny
sta (state),y
iny
sta (state),y
sec ; add 1 in make_byte_offset
jsr make_byte_offset ; get file offset+1 into regsave
ldy #st::line_off ; move regsave into line offset
lda regsave
sta (state),y
iny
lda regsave+1
sta (state),y
iny
lda regsave+2
sta (state),y
iny
lda regsave+3
sta (state),y
jmp nextchar1
jmp_nextchar:
jmp nextchar
start_lit:
getstate st::parser_st
tay
lda literal_errors,y
bne error
lda #prop_lit | prop_int | prop_num
putstate st::flags
lda #0
putstate st::str_idx
lda #lex_literal
putstate st::lexer_st ; fall thru and process same char as literal
l_literal:
jsr getchar
ldy #st::flags
and (state),y
bne goodliteral
lda (state),y
tax
getstate st::str_idx
tay
lda #0
sta (strbuf),y ; null-terminate string
txa
jsr handle_literal
bcs error
lda #lex_ready
putstate st::lexer_st
jmp parseloop ; process the same character again
goodliteral:
sta (state),y ; write back flags after and
jmp putchar
l_string:
jsr getchar
and #prop_str
beq illegal_char
cpx #$5C ; backslash
beq got_backslash
cpx #$22 ; double quote
beq got_quote
jmp putchar
got_backslash:
lda #lex_str_escape
putstate st::lexer_st
jmp nextchar
got_quote:
jsr handle_string
bcs error
lda #lex_ready
putstate st::lexer_st
jmp nextchar
illegal_char:
lda #J65_ILLEGAL_CHAR
error: rts ; error exit
l_str_escape:
lda #lex_string
putstate st::lexer_st
ldy charidx ; don't need to jsr getchar; don't need props
lda (inbuf),y
sta esc_code
cmp #$5c ; backslash
beq escape_later
cmp #'u'
beq escape_later
jsr lookup_escape
bcs illegal_escape
tax
jmp putchar
illegal_escape:
lda #J65_ILLEGAL_ESCAPE
rts ; error exit
escape_later:
lda #1
putstate st::flags ; flag indicating we need a later escape pass
getstate st::str_idx ; re-insert backslash first
tay
lda #$5c ; backslash
sta (strbuf),y
iny
beq strtoolong
lda esc_code
jmp putchar1
putchar: ; x contains char to put in string buf
getstate st::str_idx
tay
txa
putchar1: ; a contains char, y contains str_idx
sta (strbuf),y
iny
beq strtoolong
tya
putstate st::str_idx
nextchar:
ldy #st::col_num
jsr inc_state_long
nextchar1:
ldy charidx
lda (inbuf),y
putstate st::prev_char
getstate st::parser_st
cmp #par_done
beq done
lda charidx
cmp inbuflast
beq wantmore
inc charidx
jmp parseloop
wantmore:
lda #J65_WANT_MORE
rts ; end of subroutine
done: lda #J65_DONE
rts ; end of subroutine
strtoolong:
lda #J65_STRING_TOO_LONG
rts ; error exit
disp_illegal_char:
lda #J65_ILLEGAL_CHAR
rts ; error exit
disp_parse_error:
lda #J65_PARSE_ERROR
rts ; error exit
disp_exp_string:
lda #J65_EXPECTED_STRING
rts ; error exit
disp_exp_colon:
lda #J65_EXPECTED_COLON
rts ; error exit
disp_exp_comma:
lda #J65_EXPECTED_COMMA
rts ; error exit
disp_exp_obj_end:
lda #J65_EXPECTED_OBJ_END
rts ; error exit
disp_exp_array_end:
lda #J65_EXPECTED_ARRAY_END
error2: rts ; error exit
pop_and_error:
tax
pla
txa
rts ; error exit
disp_start_obj:
ldx #J65_START_OBJ
lda #0 ; index into close_states
descend:
pha
stx evtype
getstate st::parser_st2
jsr push_state_stack
bcs pop_and_error
jsr call_callback
bcs pop_and_error
pla
tax
lda close_states,x
putstate st::parser_st
lda close_states+1,x
putstate st::parser_st2
jmp nextchar
disp_end_obj:
lda #J65_END_OBJ
ascend: sta evtype
jsr call_callback
bcs error2
jsr pop_state_stack
bcs error2
putstate st::parser_st
putstate st::parser_st2
jmp nextchar
disp_start_array:
ldx #J65_START_ARRAY
lda #2 ; index into close_states
jmp descend
disp_end_array:
lda #J65_END_ARRAY
jmp ascend
disp_start_string:
lda #lex_string
putstate st::lexer_st
lda #0
putstate st::flags ; boolean for second unescape pass
putstate st::str_idx
jmp nextchar
disp_comma_array:
lda #par_ready
dca1: putstate st::parser_st
jmp nextchar
disp_comma_object:
lda #par_key
jmp dca1
disp_colon:
lda #par_ready
jmp dca1
.rodata
.define lex_tab l_ready-1, l_literal-1, l_string-1, l_str_escape-1
lex_tab_l:
.lobytes lex_tab
lex_tab_h:
.hibytes lex_tab
.undefine lex_tab
flags_prop_lit_or_num:
.byte prop_lit | prop_int | prop_num
.define dt_none disp_illegal_char-1,disp_illegal_char-1,disp_illegal_char-1,disp_illegal_char-1,disp_illegal_char-1,disp_illegal_char-1,disp_illegal_char-1,disp_illegal_char-1
.define dt_lsq disp_start_array-1,disp_start_array-1,disp_exp_string-1,disp_exp_string-1,disp_exp_colon-1,disp_exp_comma-1,disp_exp_comma-1,disp_parse_error-1
.define dt_lcur disp_start_obj-1,disp_start_obj-1,disp_exp_string-1,disp_exp_string-1,disp_exp_colon-1,disp_exp_comma-1,disp_exp_comma-1,disp_parse_error-1
.define dt_rsq disp_parse_error-1,disp_end_array-1,disp_exp_string-1,disp_exp_obj_end-1,disp_exp_colon-1,disp_end_array-1,disp_exp_obj_end-1,disp_parse_error-1
.define dt_rcur disp_parse_error-1,disp_exp_array_end-1,disp_exp_string-1,disp_end_obj-1,disp_exp_colon-1,disp_exp_array_end-1,disp_end_obj-1,disp_parse_error-1
.define dt_colon disp_parse_error-1,disp_parse_error-1,disp_exp_string-1,disp_exp_string-1,disp_colon-1,disp_exp_comma-1,disp_exp_comma-1,disp_parse_error-1
.define dt_comma disp_parse_error-1,disp_parse_error-1,disp_exp_string-1,disp_exp_string-1,disp_exp_colon-1,disp_comma_array-1,disp_comma_object-1,disp_parse_error-1
.define dt_quote disp_start_string-1,disp_start_string-1,disp_start_string-1,disp_start_string-1,disp_exp_colon-1,disp_exp_comma-1,disp_exp_comma-1,disp_parse_error-1
dispatch_tab_l:
.lobytes dt_none
.lobytes dt_lsq
.lobytes dt_lcur
.lobytes dt_rsq
.lobytes dt_rcur
.lobytes dt_colon
.lobytes dt_comma
.lobytes dt_quote
dispatch_tab_h:
.hibytes dt_none
.hibytes dt_lsq
.hibytes dt_lcur
.hibytes dt_rsq
.hibytes dt_rcur
.hibytes dt_colon
.hibytes dt_comma
.hibytes dt_quote
.undefine dt_none
.undefine dt_lsq
.undefine dt_lcur
.undefine dt_rsq
.undefine dt_rcur
.undefine dt_colon
.undefine dt_comma
.undefine dt_quote
close_states:
.byte par_key_or_close_object, par_need_comma_or_close_object
.byte par_ready_or_close_array, par_need_comma_or_close_array
literal_errors: ; needs to match parser state enum
.byte 0, 0, J65_EXPECTED_STRING, J65_EXPECTED_STRING, J65_EXPECTED_COLON
.byte J65_EXPECTED_COMMA, J65_EXPECTED_COMMA, J65_PARSE_ERROR
.code
.endproc ; parse
;; event type is in evtype.
;; Returns callback's return value in a.
;; Sets carry if return value is negative.
;; clobbers all regs.
.proc call_callback
lda inbuflast ; save caller-save regs
pha
lda charidx
pha
ldy #st::callback ; get callback into ptr4
lda (state),y
sta ptr4
iny
lda (state),y
sta ptr4+1
lda state ; state ptr is passed on stack
ldx state+1
jsr pushax
lda evtype ; event type in ax
ldx #0
jsr callptr4 ; call the C callback function (in ptr4)
tax ; save return value
pla ; restore caller-save regs
sta charidx
pla
sta inbuflast
txa
asl ; set carry if return value is negative
txa ; get return value back into a
rts ; end of subroutine
.endproc ; call_callback
;; add charidx plus carry flag to file_off and store result in regsave.
;; clobbers a, x, y.
.proc make_byte_offset
lda charidx
ldy #st::file_off
adc (state),y
sta regsave
lda #0
iny
adc (state),y
sta regsave+1
iny
lda #0
adc (state),y
sta regsave+2
iny
lda #0
adc (state),y
sta regsave+3
rts
.endproc ; make_byte_offset
;; Takes escape code in esc_code (tmp2).
;; If legal, returns escaped char in a with carry clear.
;; If not legal, returns with carry set.
;; Clobbers y, preserves x.
.proc lookup_escape
ldy #0
loop: lda escape_codes,y
beq notfound
iny
cmp esc_code
bne loop
dey
lda escaped_chars,y
clc
rts
notfound:
sec
rts
.rodata
escape_codes:
.byte $22,"/bfnrt",0
escaped_chars:
.byte $22, $2f, $08, $0c, $0a, $0d, $09
.code
.endproc ; lookup_escape
;; Handle a double-quoted string.
;; Check flags to see if it needs a second unescaping pass.
;; Clobbers all registers.
;; On success, returns carry clear.
;; On error, returns carry set with error event in a.
.proc handle_string
getstate st::flags
beq skipescape
jsr unescape_unicode
bcc skipescape
lda #J65_ILLEGAL_ESCAPE
rts ; error exit; carry is still set
skipescape:
getstate st::str_idx
tay
lda #0
sta (strbuf),y ; null-terminate string
getstate st::parser_st
cmp #par_ready
beq p_ready
cmp #par_ready_or_close_array
beq p_ready
cmp #par_key_or_close_object
beq p_key
cmp #par_key
beq p_key
lda #J65_PARSE_ERROR
sec
error: rts ; error exit
p_ready:
lda #J65_STRING
sta evtype
jsr call_callback
bcs error
getstate st::parser_st2 ; get next parser state in a
putstate st::parser_st
clc
rts ; success exit
p_key: lda #J65_KEY
sta evtype
jsr call_callback
bcs error
lda #par_need_colon
putstate st::parser_st
clc
rts ; success exit
.endproc ; handle_string
;; unescape \\ and \u in the string buffer.
;; returns carry set on error. clear on success.
;; clobbers all registers.
.proc unescape_unicode
getstate st::str_idx
sta tmp2
ldy #0
sty tmp1
loop: cpy tmp2
beq done
lda (strbuf),y
iny
cmp #$5c ; backslash
beq escape
loop1: sty tmp5
ldy tmp1
sta (strbuf),y
inc tmp1
ldy tmp5
jmp loop
escape: cpy tmp2
beq error
lda (strbuf),y
iny
cmp #$5c ; backslash
beq loop1
cmp #'u'
beq unicode
error: sec
rts
unicode:
jsr read4hexintosreg
bcs error
jsr movesregtolong1
lda (strbuf),y
cpy tmp2
beq bmp
cmp #$5c ; backslash
bne bmp
iny
cpy tmp2
beq bmp0
lda (strbuf),y
cmp #'u'
beq check_surrogate
bmp0: dey
bmp: jsr long1toutf8
jmp loop
bmp1: pla
tay
jmp bmp
check_surrogate:
jsr is_sreg_left_surrogate
bcc bmp0
tya
sub #1
pha
iny
jsr read4hexintosreg
bcs bmp1
jsr is_sreg_right_surrogate
bcc bmp1
pla
jsr combine_surrogates
jmp bmp
done: lda tmp1
putstate st::str_idx
clc
rts
.endproc ; unescape_unicode
;; reads 4 hex digs from strbuf at y into sreg.
;; (buffer length is in tmp2)
;; on success, carry clear, leaves y pointing after 4 digs.
;; on failure, carry set.
.proc read4hexintosreg
ldx #4
loop: jsr shift_sreg_left_4bits
jsr or1hexintosreg
bcs done
dex
bne loop
clc
done: rts
.endproc ; read4hexintosreg
;; clobbers a, preserves x and y.
.proc shift_sreg_left_4bits
lda sreg
asl a
rol sreg+1
asl a
rol sreg+1
asl a
rol sreg+1
asl a
rol sreg+1
sta sreg
rts
.endproc ; shift_sreg_left_4bits
;; reads 1 hex dig from strbuf at y into low 4 bits of sreg.
;; (buffer length is in tmp2)
;; on success, carry clear, leaves y pointing after digit.
;; on failure, carry set.
.proc or1hexintosreg
cpy tmp2
beq fail
lda (strbuf),y
iny
jsr hex_dig_to_nibble
bcs fail
ora sreg
sta sreg
clc
rts
fail: sec
rts
.endproc ; or1hexintosreg
;; converts ascii char in a to nibble in a.
;; sets carry if not a hex digit.
;; preserves x and y.
.proc hex_dig_to_nibble
cmp #'0'
blt fail
cmp #'9'+1
bge tryupper
sub #'0'
clc
rts
tryupper:
cmp #'A'
blt fail
cmp #'F'+1
bge trylower
sub #'A'-10
clc
rts
trylower:
cmp #'a'
blt fail
cmp #'f'+1
bge fail
sub #'a'-10
clc
rts
fail: sec
rts
.endproc ; hex_dig_to_nibble
;; zero-extends sreg into long1.
;; clobbers a, preserves x and y
.proc movesregtolong1
lda sreg
sta long1
lda sreg+1
sta long1+1
lda #0
sta long1+2
sta long1+3
rts
.endproc ; movesregtolong1
;; converts long1 to utf8 in strbuf at tmp1.
;; (output index is in tmp1)
;; preserves y.
.proc long1toutf8
sty tmp5
ldy tmp1
lda long1+2
bne len4
lda long1+1
beq latin1
cmp #8
bge len3
len2: ldx #1
jsr utf8_shift
lda long1
and #%00111111
ora #%10000000
sta long1
lda long1+1
and #%00011111
ora #%11000000
sta long1+1
ldx #1
jmp done
len3: ldx #1
jsr utf8_shift
ldx #2
jsr utf8_shift
lda long1
and #%00111111
ora #%10000000
sta long1
lda long1+1
and #%00111111
ora #%10000000
sta long1+1
lda long1+2
and #%00001111
ora #%11100000
sta long1+2
ldx #2
jmp done
len4: ldx #1
jsr utf8_shift
ldx #2
jsr utf8_shift
ldx #3
jsr utf8_shift
lda long1
and #%00111111
ora #%10000000
sta long1
lda long1+1
and #%00111111
ora #%10000000
sta long1+1
lda long1+2
and #%00111111
ora #%10000000
sta long1+2
lda long1+3
and #%00000111
ora #%11110000
sta long1+3
ldx #3
jmp done
latin1: lda long1
bmi len2
ldx #0 ; length 1, already in the right format
done: jsr writeutf8
sty tmp1
ldy tmp5
rts
.endproc ; long1toutf8
;; writes the first x+1 bytes of long1, in reverse order,
;; to strbuf, starting at y. advances y.
;; clobbers a, x.
.proc writeutf8
lda long1,x
sta (strbuf),y
iny
dex
bpl writeutf8
rts
.endproc ; writeutf8
;; shift the last 4-x bytes of long1 left by 2 bits.
;; clobbers a, x. preserves y.
.proc shift_left_by_2
stx tmp6
jsr shift_left_by_1
ldx tmp6
shift_left_by_1:
asl long1,x
loop: php
cpx #3
beq done
inx
plp
rol long1,x
jmp loop
done: plp
rts
.endproc ; shift_left_by_2
;; shift the last 4-x bytes of long1 left by 2 bits.
;; shifts in the top two bits from the previous byte, too.
;; clobbers a, x. preserves y.
.proc utf8_shift
dex
txa
pha
jsr shift_left_by_2
pla
tax
lsr long1,x
lsr long1,x
rts
.endproc ; utf8_shift
;; preserves y.
;; sets carry if sreg is a left surrogate.
.proc is_sreg_left_surrogate
lda sreg+1
and #$fc
cmp #$d8
beq yes
clc
rts
yes: sec
rts
.endproc ; is_sreg_left_surrogate
;; preserves y.
;; sets carry if sreg is a right surrogate.
.proc is_sreg_right_surrogate
lda sreg+1
and #$fc
cmp #$dc
beq yes
clc
rts
yes: sec
rts
.endproc ; is_sreg_right_surrogate
;; combine left surrogate in long1 with right surrogate in sreg.
;; result in long1. preserves y.
.proc combine_surrogates
lda long1+1
and #3
sta long1+2
lda long1
sta long1+1
asl long1+1
rol long1+2
asl long1+1
rol long1+2
lda sreg
sta long1
lda sreg+1
and #3
ora long1+1
sta long1+1
inc long1+2
rts
.endproc ; combine_surrogates
;; parse signed integer in strbuf (length in str_idx).
;; on success, carry clear and result in long1 (regsave).
;; on integer overflow, carry set and overflow set.
;; on illegal character, carry set and overflow clear.
;; clobbers a, x, and y.
.proc parse_signed_integer
ldy #0
lda (strbuf),y
cmp #'-'
beq negative
jsr parse_unsigned_integer
bcs done ; overflow of 32-bit int, C and V are set
bit long1+3
bpl done ; if hi bit is clear, it is okay
not_okay:
sec ; otherwise, set carry and overflow
bit an_rts
done:
an_rts: rts
negative:
iny
jsr parse_unsigned_integer
bcs done ; overflow of 32-bit int, C and V are set
lda #$7f
bit long1+3
bpl okay ; if hi bit is clear, it is okay
bne not_okay ; if hi byte is not $80, it is not okay
lda long1+2
ora long1+1
ora long1
bne not_okay ; only okay if 3 least significant bytes are 0
okay: jsr resteax
jsr negeax
jsr saveeax
clc
jmp done
.endproc ; parse_signed_integer
;; parse unsigned integer in strbuf, starting at y.
;; on success, carry clear and result in long1 (regsave).
;; on integer overflow, carry set and overflow set.
;; on illegal character, carry set and overflow clear.
;; clobbers a and y. preserves x.
.proc parse_unsigned_integer
lda #0
sta long1
sta long1+1
sta long1+2
sta long1+3
loop: tya
ldy #st::str_idx
cmp (state),y
bge done
tay
jsr multiply_long1_by_10
bcs overflow
lda (strbuf),y
jsr hex_dig_to_nibble
bcs error
jsr add_a_to_long1
iny
bcc loop
overflow:
bit an_rts ; bit on an rts instruction will set overflow
an_rts: rts ; carry and overflow are both set
done: clc
rts ; success: carry clear
error: clv
sec
rts ; carry set, overflow clear
.endproc ; parse_unsigned_integer
;; multiplies long1 by 10. clobbers a and long2. preserves x y.
;; returns with carry set if result overflows a 32-bit unsigned long.
.proc multiply_long1_by_10
jsr shift_long1_left_by_1
bcs done
lda long1
sta long2
lda long1+1
sta long2+1
lda long1+2
sta long2+2
lda long1+3
sta long2+3
jsr shift_long1_left_by_1
bcs done
jsr shift_long1_left_by_1
bcs done
lda long1
add long2
sta long1
lda long1+1
adc long2+1
sta long1+1
lda long1+2
adc long2+2
sta long1+2
lda long1+3
adc long2+3
sta long1+3
done: rts
.endproc ; multiply_long1_by_10
;; shifts long1 left by 1. clobbers a; preserves x and y.
.proc shift_long1_left_by_1
asl long1
rol long1+1
rol long1+2
rol long1+3
rts
.endproc ; shift_long1_left_by_1
;; add a to long1. sets carry if result overflows an unsigned long.
;; clobbers a; preserves x and y.
.proc add_a_to_long1
add long1
sta long1
lda long1+1
adc #0
sta long1+1
lda long1+2
adc #0
sta long1+2
lda long1+3
adc #0
sta long1+3
rts
.endproc ; add_a_to_long1
;; parse "null", "false", or "true" in strbuf (length in str_idx).
;; on success, carry clear and a contains event number.
;; on failure, carry set. clobbers x and y.
.proc identify_literal
getstate st::str_idx
cmp #4
beq len4
cmp #5
beq len5
fail: sec
rts
len4: lda #<str_null
ldx #>str_null
ldy #3
jsr compare_strings
beq got_null
lda #<str_true
ldx #>str_true
ldy #3
jsr compare_strings
bne fail
lda #J65_TRUE
clc
rts
got_null:
lda #J65_NULL
clc
rts
len5: lda #<str_false
ldx #>str_false
ldy #4
jsr compare_strings
bne fail
lda #J65_FALSE
clc
rts
.rodata
str_null:
.byte "null"
str_true:
.byte "true"
str_false:
.byte "false"
.code
.endproc ; identify_literal
;; compares string (of length y+1) at strbuf with string pointed to
;; by a (lo byte) and x (hi byte).
;; returns with zero flag set if strings match, or zero flag clear
;; if they do not.
;; clobbers ptr1.
.proc compare_strings
sta ptr1
stx ptr1+1
loop: lda (strbuf),y
cmp (ptr1),y
bne done
dey
bpl loop
lda #0 ; set zero flag
done: rts
.endproc ; compare_strings
;; Handle a literal (a number, or null, true, or false).
;; On entry, a should contain flags. (prop_lit, prop_int, prop_num)
;; Clobbers all registers.
;; On success, returns carry clear.
;; On error, returns carry set with error event in a.
.proc handle_literal
tax
getstate st::parser_st
cmp #par_ready
beq p_ready
cmp #par_ready_or_close_array
beq p_ready
parse_err:
lda #J65_PARSE_ERROR
sec
error: rts ; error exit
p_ready:
txa
bit flags_prop_lit
bne keyword
bit flags_prop_int
bne integer
number: lda #J65_NUMBER
do_callback:
sta evtype
jsr call_callback
bcs error
getstate st::parser_st2 ; get next parser state in a
putstate st::parser_st
clc
rts ; success exit
integer:
jsr parse_signed_integer
bcs not_integer
ldy #st::long_val ; copy long1 to long_val
lda long1
sta (state),y
iny
lda long1+1
sta (state),y
iny
lda long1+2
sta (state),y
iny
lda long1+3
sta (state),y
lda #J65_INTEGER
jmp do_callback
not_integer:
bvs number
jmp parse_err
keyword:
jsr identify_literal
bcs parse_err
jmp do_callback
.rodata
flags_prop_lit:
.byte prop_lit
flags_prop_int:
.byte prop_int
.code
.endproc ; handle_literal
;; push a onto the state stack.
;; carry clear on success.
;; carry set on error, with error event in a.
;; clobbers x, y.
.proc push_state_stack
tax
getstate st::stack_idx
ldy #st::stack_min
cmp (state),y
blt stack_full
tay
txa
sta (state),y
dey
tya
putstate st::stack_idx
clc
rts
stack_full:
lda #J65_NESTING_TOO_DEEP
sec
rts
.endproc ; push_state_stack
;; pop the state stack.
;; carry clear on success, with popped state in a.
;; carry set on error, with error event in a.
;; clobbers x, y.
.proc pop_state_stack
getstate st::stack_idx
tay
iny
beq stack_empty
lda (state),y
tax
tya
putstate st::stack_idx
txa
clc
rts
stack_empty:
lda #J65_PARSE_ERROR
sec
rts
.endproc ; pop_state_stack
;; increment the long at state+y to state+y+3 by 1.
;; clobbers a and y.
.proc inc_state_long
lda (state),y
add #1
sta (state),y
bcc done ; short circuit for speed
iny
lda (state),y
adc #0
sta (state),y
iny
lda (state),y
adc #0
sta (state),y
iny
lda (state),y
adc #0
sta (state),y
done: rts
.endproc ; inc_state_long
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_get_string ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; const char * __fastcall__ j65_get_string(const j65_state *s);
;; (string buffer is the second 256 bytes of state, so all we have
;; to do is increment the high byte of the argument)
.proc _j65_get_string
inx
rts
.endproc ; _j65_get_string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_get_length ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; uint8_t __fastcall__ j65_get_length(const j65_state *s);
.proc _j65_get_length
sta ptr1
stx ptr1+1
ldy #st::str_idx
lda (ptr1),y
ldx #0
rts
.endproc ; _j65_get_length
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_get_integer ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; int32_t __fastcall__ j65_get_integer(const j65_state *s);
_j65_get_integer:
ldy #st::long_val+3
;; copies the value at ax+y-3 thru ax+y to eax
get_long:
sta ptr1
stx ptr1+1
lda (ptr1),y
sta sreg+1
dey
lda (ptr1),y
sta sreg
dey
lda (ptr1),y
tax
dey
lda (ptr1),y
rts
; end _j65_get_long
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_get_line_offset ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; uint32_t __fastcall__ j65_get_line_offset(const j65_state *s);
.proc _j65_get_line_offset
ldy #st::line_off+3
jmp get_long
.endproc ; _j65_get_line_offset
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_get_line_number ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; uint32_t __fastcall__ j65_get_line_number(const j65_state *s);
.proc _j65_get_line_number
ldy #st::line_num+3
jmp get_long
.endproc ; _j65_get_line_number
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_get_column_number ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; uint32_t __fastcall__ j65_get_column_number(const j65_state *s);
.proc _j65_get_column_number
ldy #st::col_num+3
jmp get_long
.endproc ; _j65_get_column_number
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_get_current_depth ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; uint8_t __fastcall__ j65_get_current_depth(const j65_state *s);
.proc _j65_get_current_depth
ldy #st::stack_idx
sta ptr1
stx ptr1+1
lda #255
sub (ptr1),y
ldx #0
rts
.endproc ; _j65_get_current_depth
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_get_max_depth ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; uint8_t __fastcall__ j65_get_max_depth(const j65_state *s);
.proc _j65_get_max_depth
ldy #st::stack_min
sta ptr1
stx ptr1+1
lda #0
sub (ptr1),y
ldx #0
rts
.endproc ; _j65_get_max_depth
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; j65_get_context ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; void * __fastcall__ j65_get_context(const j65_state *s);
.proc _j65_get_context
ldy #st::context+1
sta ptr1
stx ptr1+1
lda (ptr1),y
tax
dey
lda (ptr1),y
rts
.endproc ; _j65_get_context
================================================
FILE: tests/create-testfile-disk-image.pl
================================================
#!/usr/bin/perl -w
# 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.
use strict;
use FindBin;
# Assumes that the "ac" shell script wrapper for Apple Commander
# is on your PATH:
# https://applecommander.github.io/install/
my $ac = "ac";
my $diskimage = "testfile.po";
my $tests = $FindBin::Bin;
chdir ($tests);
my $blue = "\e[34m";
my $green = "\e[32m";
my $red = "\e[31m";
my $off = "\e[0m";
sub mysystem {
my @cmd = @_;
print join(" ", @cmd), "\n";
if (system (@cmd) != 0) {
if ($? == -1) {
die "$red*** fatal: $!$off\n";
} elsif ($? & 127) {
die (sprintf ("$red*** fatal: signal %d$off\n", $? & 127));
} else {
die (sprintf ("$red*** fatal: exit code %d$off\n", $? >> 8));
}
}
}
sub print_heading {
my $str = $_[0];
print "$blue*** $str$off\n";
}
print_heading "Creating disk image";
mysystem ("rm", "-f", $diskimage);
mysystem ($ac, "-pro140", $diskimage, "testfile");
print_heading "Adding test program";
my $program = "testfile.system";
mysystem ("$ac -as $diskimage $program < $program");
print_heading "Adding JSON files";
my @json = split (' ', `echo file??.json`);
foreach my $json (@json) {
mysystem ("$ac -p $diskimage $json TXT < $json");
}
================================================
FILE: tests/file00.json
================================================
{ "hello", "error" }
================================================
FILE: tests/file01.json
================================================
{ "mismatched": true ]
================================================
FILE: tests/file02.json
================================================
{ null: "bad" }
================================================
FILE: tests/file03.json
================================================
{ "error": $foo }
================================================
FILE: tests/file04.json
================================================
[[[[[[[[[[[[[[[[["nesting"]]]]]]]]]]]]]]]]]
================================================
FILE: tests/file05.json
================================================
{
"multiple": 1,
"lines": 2,
"trouble":: 3
}
================================================
FILE: tests/file06.json
================================================
[
"an",
"extra",
"comma",
]
================================================
FILE: tests/file07.json
================================================
[ "a string which is way too long: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!!?ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210 :gnol oot yaw si hcihw gnirts a" ]
================================================
FILE: tests/test-file.c
================================================
/*
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.
*/
#include <conio.h>
#include "json65-file.h"
static uint8_t scratch[1024];
static char filename[80];
static void errfunc (FILE *err, void *ctx, int8_t status) {
fprintf (err, "Unknown error %d\n", status);
}
static int8_t callback (j65_parser *p, uint8_t event) {
return 0;
}
int main (int argc, char **argv) {
FILE *f;
int8_t status;
uint8_t width, height, i;
screensize (&width, &height);
for (i = 0 ; i < 100 ; i++) {
snprintf (filename, sizeof (filename), "file%02d.json", i);
printf ("\nParsing %s\n\n", filename);
f = fopen (filename, "r");
if (! f)
break;
status = j65_parse_file (f, scratch, sizeof (scratch),
NULL, callback, 16,
stderr, width, filename, errfunc);
fclose (f);
cgetc();
}
return 0;
}
================================================
FILE: tests/test-print.c
================================================
/*
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.
*/
#include <string.h>
#include "json65-print.h"
static char buf1[2048];
static char buf2[2048];
static j65_parser parser;
static j65_tree tree;
static const char infile[] = "test-print.json";
static const char outfile[] = "json.test.print.tmp";
static int do_test (void) {
int8_t status;
size_t len;
FILE *f;
int ret;
j65_init_tree (&tree);
j65_init (&parser, &tree, j65_tree_callback, 255);
len = strlen (buf1);
status = j65_parse (&parser, buf1, len);
if (status != J65_DONE) {
fprintf (stderr, "status %d\n", status);
return 1;
}
f = fopen (outfile, "w");
if (! f) {
fprintf (stderr, "Couldn't open file '%s' for writing\n", outfile);
return 1;
}
ret = j65_print_tree (tree.root, f);
fputc ('\n', f);
fclose (f);
if (ret < 0) {
fprintf (stderr, "Error writing file\n");
return 1;
}
f = fopen (outfile, "r");
if (! f) {
fprintf (stderr, "Couldn't open file '%s' for reading\n", outfile);
return 1;
}
if (! fgets (buf2, sizeof (buf2), f)) {
fprintf (stderr, "Couldn't read file\n");
return 1;
}
fclose (f);
if (0 != strcmp (buf1, buf2)) {
fprintf (stderr, "strings not equal:\n%s%s\n", buf1, buf2);
return 1;
}
j65_free_tree (&tree);
return 0;
}
int main (int argc, char **argv) {
FILE *f;
int badness = 0;
f = fopen (infile, "r");
if (! f) {
fprintf (stderr, "Couldn't open file '%s' for reading\n", infile);
return 1;
}
while (fgets (buf1, sizeof (buf1), f)) {
badness += do_test ();
}
fclose (f);
if (badness == 0)
fprintf (stderr, "Success!\n");
return badness;
}
================================================
FILE: tests/test-print.json
================================================
["Hello","World!"]
{"foo":"bar","nested":[1,2,3]}
"Just a string"
5
false
[0.32572436,true,-11186217319148845949,true,0.27938694,0.75560504]
[]
{"$":1.8247128e-2,"9Sz2D\r":12737293809857351644,"\u0016c\u0004\f":false}
["\u0002=\"{",null,"pb\u0005","\u0011r't",9.4976306e-2,"'"]
{"":7603252728548869453,"{\u001e":"\n","<\u001536%E":274716178498089137,"&lB":false,"%(v\u001f\u0001t":-13982809748860309521}
{"c+F![":"","\u0016\n\u0001\u0004UB":"<","":4.665512e-2,")\nX3\u0019":"","EK%":"\u0011\u0013\u0014","vg\u00047m":false}
{"odG\u0013C":0.44743448,"@nuwx":false,"S)5aT":"","}\u0004\u001fa":-12152612225799926450,"\f0m\u0006":-10691034618040127166}
[0.5863078,"OJ",0.15542328]
["Du\u0014",8827766992909995585,0.2134161,0.90028864,"C"]
["4t4(w","+BPq",false,45313040627093027]
["\u0018Q~F",0.33080858,"\u001cNu9 ","f\u001a5H2R",1707525377738296452,0.924928,true,false,false,true,0.9340122,0.5125267]
["Q4\u0004",null,"Hw","h","",-3312958782175383110,false,"`V0u~t",-17853009426579656790,true,"\u0012\u001fKe5\u0001",null]
{"\t\u0001FK\u0010\u0012":"E"}
[true,3213833407962205374,false,0.9740944,null,0.69657785,-12761276671025544045,false,4932447720730719695,2646858136238674420,225658451089210750]
[10790624673763905503,12435287114942852151,-7403136589746519035,14505181554639941694,"'u"]
{}
{"O\u000e\u0019t\u0018c":-3460634944561346323,"'yXb\u001e":false,"=#+Q":0.11444086,"\u0015F\u0006":false,"<gZP\u001c\u001f":-8960435077329510243,"szu":0.6044294}
{"&.\tV":"a","N":-17694422251758191599,"{\u001c^\u000b":"&=\u0016.K}","\u001f}~~[\t":13764838241451311043,"b\u0010)!s\u0013":4115161054381493887,"pE;\u0015B7":false,"}":"\u0003\u0002H\f@`","ocV>V^":814108585199004714,"\rTA":0.47725976,"{\"\u0001\u001aw_":"Y.*","UT\u0018":false}
{"*":0.8523169,"\u0010,":false,"\u00079":0.8049666}
[-5327988121100041754,false,-13018219928679153234,-2331534658977772737,0.78117466,"#a\u00190'","",false,4.1714847e-2,0.56394154]
{"":-15521982000753555023,"||\u0003e\u0013\u000b":-1690917563470919131,"?":12979257830205574254,"HL":true,"O~":7713027951886179383,"H\u0007":"r\f=P","4\u0005Ws":0.19205546,"F\u000f(ox4":-5887537840175889906,"H+\u0006l\u0010":"\u0017","N":null,"":false}
[-12036274170737061087,"G,Xc~w","l\b",false,true,-14114159388375863713,-17049232511652779296]
["T","",4.8560143e-2,10885120778919441467,0.28279763,15914637999585694553,9020298222284681635,false,"\rw+m",null]
[-17968810982863699742,false,null]
{"j\u000b7(=":"d\u000beSa","\u001fn=8^|":""}
{"":-4299274683684051628,"":0.89675933}
[0.69776416,6.477058e-2,0.6377239,4.8692107e-2,true]
["B_Xm7",0.64527464,[],null,{"":0.60033214,"\u000bj:\u000b/":-17464052542706863991,"\u0014&cI\u0012+":[],"P":[]}]
[false,8.072078e-3,0.7576571,null,["\u0004</e:","f;5\b]",0.13011688,"-\u0016{L^r"]]
[false,"*Z"]
{"":-14448800448673676895,"@y\u001aq}\u0002":0.11866385,"K4V;\u0014":[["w",0.2579152,"M","",8313590187149240626],"$>h",["X",0.45122665,""],0.11203796]}
[true]
["\u001cDP",[0.8626665,12114414041893822438]]
{"*\u0016\f1}":{"1\u001b":0.36654127,"\u0007;{\"":{"":"41e\u0018","\u0015fT;":11574806650096644037,"Z":"_0\u0006&X"},"vE+ ":2935046322892156719,"":""}}
{"?+hf`":{}}
[null,[true,-9913222499256921221,null,15743710446996284792],{},true]
[[[-9627130801059221053,null],4065208257197815031,null],-9767422520931038936]
[[false,false,0.95248777],true,0.19347006]
[true,null]
[false,false,0.4905488,1.1336803e-3,"J"]
[0.25115472,null,"",0.8550764]
{"b\tH":7491698839064921711,"":[0.9998612,false,null,0.665416,17844276928723417672],"\u0005t":null}
[{"]\u001fM\u0001":0.62482804,"?y ":[false,"T",0.40800726],"":[0.2510013,false,{"AH m":[null]},{"V":"7"}],"h\u001e%C":0.30841583,"":"!s\u0013"},{"Vy\u0010@":"7>","":"\u0017I\u001d","W7M2A":{"C\u0005u:J9":{"!%lu0":{"|":1773417749156486461,"":0.7282211,"\u0016<Z\u001a=":{"\nX":{},"CG":"^:s","\u00129\u0011z,":"2\u0007w\u0002D","\u001c/":14555262098322473384,"":-5946533795406649298},"\\<A":17937647869425473647,"4\u0010:X":{}},"r":0.89700264,"zal\u0001":[]},"":"\\\u000e\u0014","V":{"nn":[],"@\u0019|T":"R\u0006"},"u{<.5":["U\b\u0007s~",{"W)":false,"v\f$Wi":-17612115454251660451,"G\u0006\u0019\u0003mk":-9458453112674665176,"Y\u0001r":{"":{"Z*\u001a\u0015_i":"_6\u001c~\"}","\"4":{"":0.3258859,"\u000fo[RL":2738684748814013921,".":0.5259006,"f":0.28348464},"\u0019\u001a}":0.9390163,"g- ":["1gfy4\u000b",0.13463932,"\tv",false],"m-9\u001e":{",b\u0006":0.35202783}},"9j!":0.6527836}},"m\u000eF",15401944105642734126,0.100584686]},"":{"=":{" @\u0018[\u0013":-4798237755001598196,"qZ\b0":"o\f\u000e"},"2s":false}},[-17026305678075551850]]
{"=u_":0.10595447,"e86|?":16584676174349563655,"Y\f\ni":true,"\t":[]}
{"Y\u000f\u0007":null}
{"a:kK\u00060":"=:$&)\u0015","w\u001f\n\u0016f'":{"\n!&":"\u000eUl,o%","BD":11168338163327932912,"aUN->O":0.7322229,"O\u0015o%4":[5635062097326633238],"\f\"":"L\u00040\u0002"},"oF\u00110\u001es":{"@*3A0\u0010":-11941081127894283313,"\u0010":[1.052928e-2,[true,-18053543071630053160,1.9187033e-2,{}],false,[["\u0001\u0014$|",{"@V\b":"\f\u0006:\u001fv|","E\u001b\"c\u0006\n":"\u0019B%\n","5\f#*\u0014O":["<l",0.21901178],"\u001b+mK\u0001\u0002":{"\u001b\bt":"","V\u0014\u0003\u001dG\u0005":0.6024258,"":0.6851842},"U=A":-14471741596704654577},["",0.59391403]],{},"u"]],"":false},"":11271894647333079509,"4)}":12978190940517445918}
[[{},{"\u0001^[":0.6274655,"s\ru":-16285590633424424013,"\u0005_#":-2902719902988987317},4.6498835e-2,[false,"vP"],0.4098791],null,"ccJ]",{"x~M8\u000b":"Kl\nrl@","L":0.5812448,"":[12621578391773795701,-14768389065015598213],"\u0004R\u001b\u0011\u0018-":null}]
[null]
{"9\u000e":"v}NV","~\u0016t":null}
{"":5.3502917e-2,"":0.7454183,"\u001aq~":[]}
{"]":null,"D":"l0L","~*uN":[],"":9954274599782580648}
{"":0.9339974,"\u0013":4739434292161533768,"/%3yF'":4208093120411332727,"\u0012V+Im":{"~PUs\u00151":0.4505244,"o\u0015\u0006":"!C","(^":false,"":0.7402246,"c\buX":[["\u001cQ[%>","x\u0014M9",[-10952946846407602584],-17898030190354030870],"e~|5",[false,-6621870650021842955,0.105413735],null,{"m":["",false,{"i":[-11305999548229241663,"",0.7880725,"",7620683624483849005],"Ve\u0006c":[[[false,"K4\u0018",false],[[[3227070858722758620,[13811021058727279879,true,true,0.71696544,{"\u000b":15556099159842916855}],0.19199622,[]],"v",null],6.888121e-2,[14372151898541712850]]],{"A^\u0006i\u001a\t":{"":0.32023412,"":[0.55937934,17929676292327432669,-7393696966288757142,true,0.41084045],"[\u0007*":[],"}kt\u0012um":619648060789260132,"vvf":0.42513674}},"^'U5",6631621892532225136,[]]," R\u001c\u0013\u0002":"885(","\u0013{_W":" ! #c"}],"\u0016;":null,"":[{"LW\n\u0017$":-4721124271466594444,"\u001a}":16564997567000030707,"B":{"O;\u001e":[],"":{"A":{"}F":0.39923227,"\u00132`\u001en":0.85526997},"\u000fZG1H\\":true},";\u0013E\t\u0018":[[],0.21486646,{".6t":[{"e6":null,"%\u0018\f\r8W":"~dsQ\u001d"},false,0.3842417],"]\f8e\\":null},[],false]},"5+":"8","&\\4":true},"(D"],"in,=A":"DW","'\u001b":18160291427748464709}]},"$Ao>":0.79443836}
["=",0.11248773,4514021307219340329,true]
[{"\u0019e\u0011zt":{"IB\u0014":true,"*E^":{},"_$~":true},"}\u0019":{"iB''K`":"+b\u0019E\u000eE","\u001c9":"k5$SK,","g":{"{":[[],false,0.93499947],"P":0.3487832,"":true,"@\\Fm/5":true,"i":[[-5619408379903070830],true,false]},"(xk9W":{}},"\u0018>":true,"":5128100828239114421,"]\u0003`5":true},true]
[14162558928204775417,false,"!\u0016\u0011"]
{"ctk?":[false,{"9":[],"\"b^S":false,"elOrO\u000b":{"\u0006FL?j7":{},"(":null}},false,0.21888393,"\u000113"],"SW\\":["y3\u0002=%"],"g\u0002'/gS":"=%5H\u000e","\u0002m\u0001":-5775961272864050555,"NS<k1":0.5800585}
[0.86506915,0.745442,[[{"":"","\u001far":16119537991642233069},{"":[null],"Iz":false,"\u001e":"URMA[$"},0.5963315],[],"5.rW\u0017",{"P":-10068242471433579683," \u0013{f\u000e":0.392839,"Y\b\u0005Z":"","\u001f":[],"#p":0.24786073}],0.38616395,[]]
["4T1\r@",0.46275347,false]
{"hly'l":true}
{"":[false],"":18311605067220884321,"\\0":{"J#\u0011":"\b","-":0.48263913,"":[]}}
[0.9280277,"P"]
[true,{"G\\vm":true,"7<{\u0013m@":""}]
{"b":-17176440223338641234,"{~#1":"d","T&":0.3095814}
[["5w\u0012Y\n",-3465947354667783786,true],0.58169204]
{"\u0007#\u001eSf":true,"o@i":{"\u0003P\u0007r!~":{},"\u0019bx5":-5870705663361752808,"<T'\u0012\t":-8253887455420531740,"v-G\n%b":17303148683879179163,"D0":[12701047572705528383,"P^cW\u001d",{"X":"g\u0016s","":"k[\"","Cyz\n\u0004":0.2698384},{"VE":0.10767931,"\\cH,?":{"l":"B\u0019\bd ","B$\\":null},">-#^":{}}]},"^z0 ":{"S\b":-15150394133378043565,"})\u001c\n9\u0016":null}}
[""]
{"z\u0016":0.65568596}
[0.7439682,false,{"*\r\u0018_F":"Q\u0014Rok","8":[0.5402592,"_/A\n?(","\\5E\u000f"],"":[{"XtR1O":[{"\u0010;\u0016\\\u000bB":null,"?h\nA(?":true},[0.61750627,[true,-15962398617127831102,[],-8592170384657825370,[]],{"i":[0.24247152,null,"Vo\u0012b\u0016_"],"\u0003\u0004f":"RSZZ&"},1218096336475644177],17017452515666078705,0.46202022,0.30780977],"\u0012":"\n(H\r|s","Wl;*":{"\fR8":-8961150626459389675,";r":{"8":["o",11470537646784762752,4592061587037721434,0.50926375]},"f\u001b!=-7":"q\u000b","xHU)0":[],"&U":[]},"<<1\f#x":{"D\f":{"r\u001cLm":"= Dpj","\r\u0014d":[-17485124139144911826,[false],0.5664439],"rxJ.b":{"~\u001c;":"","G$:\n":"\u00071W"},"zEBx":0.9278375,"":[false,"Q","-\u0004",0.21181405,0.38027972]},";?E":{"\u001fz":0.73130953,"":11888366427098764180,",G|U":0.53782547}},"-ls\u000fp":"f\u000f"}]," ~.x(":[null]},0.46352834,"x"]
[null,{" F7\u0010":12459903973742877697,"\u0005X":0.36702603},"\u0017R%\u0007",-13502214511406886705,"\u001db"]
["%\u001bj#\u000e'",0.29517365]
{"J\u0014'6QK":[6.354201e-2],"\u0001":0.23121226,"UiN\"r":[{"B\u00136VZ":null,">":null,"y":false},["",null,true,-5570114783189784073,{"~\u0012\f:":0.37777317," ":null}],[false],true],"6N\u0012":-8156302497732879601}
{"T{|7#":false,"$>":"\u001cB?8"}
{"1}\fEU":{"4\u0017\u001f\u0007S":209123727378233237}}
[[[{"?;\u000b3":-518309841821504454,"!FN)\u0007":["\n&\u001b",[{"":-2921903025505147040,")#X":-12463943729709922411,".\tr":false,"p$":{"luP(T":15091199490892917922,"\u0019~\u000f\u000f ":{"egYH\u000e~":-14291716192989733769,"6":-5227850293455434343,"m;S":null,")":"m\u001b@kj9"},"M\u0012%<o":"/e)I$","\t":[]}},0.15512782],true],"Y}GvcE":13488126456154209821,"FM}I":true,"<1fb(":{",\u0003@gV":false,"FYC-":"/\n\u001bb","#\b":true}},{"m*4\u000eH":"&\u0018$\u001a9\r","k":{"\nTWm":0.88612574,"\u0005(^\u001a\f\u001c":true,"l]e":false,"\u0006q":true}},true,"d\u0017U4",{}],{"nU[":[10911731097193658029,0.6481947],"":0.5755147,"\t":12465806549772274316,"\u001dzK#?$":{"q":-10484987948483073532}},"k%d`&",3044966341192108219],"Uk\u0010L",{"\fg":false,"o":16369525926792919602,"(\u0004V\"[\t":"-K"},"Y","\u001e[E+"]
{"5\u0002\f":"#&5t*","b{6\u001a":-15275213607943662689,"Y;":17265934196169125293,"\nQlCs9":null,"\u0018(:":true}
[{"|":0.68765,"\u001eISgI\u0003":[false,"CM=\u0006\u00039"],"\u0014x":{"4),":[0.35985446]},"F-\u0013":false,"gkH\u0001":[true,"a.'}&j"]},false,{"\u0015\"F6":[[[],[7.6780796e-2,{"nJ":{},"\u0011kgB":[0.8854914,[true,":P",["6B&M",[[0.9121036,"\f\u00158",15364012209050718127,[true,"\r5\u000e"]]]],"mtWhem",-8179481715501543552]],"D$X&R@":2255881672456517052}],{"":"'NF~\u000eG","":[0.26298237,7752763682448092000,"C",true],"Qx$\u0005(@":null,"/":"","e\u001e{\u000e":"2"},true],"\u0006KANq\u000f"],"5.\u0004\u001f":14849978413013421257},3.3628285e-2]
{"r;s":-6593165038148587118}
[-8006884604484209542]
{"F'R&wn":0.82659614}
{"suU\u001f":0.8861345,"A\u0007rK":[{},[-6776522806699827829,-10267898355349287368],"",false],"\u001fu4NH":-2216197903302261055}
{"\u001bZ`":0.66861683,"":0.48398995,"V":0.30915624,"+":-1026948343628585707,"y:\u0005X":0.2573502}
{"{~":"","JV1\t(B":",N\u0018|_"}
[true,[],4.1036606e-2]
[0.73778456,{"/_E":[false],"s":0.81533575},"!?9dP",true,false]
{"":["i"],"\u0018Lof":{},"\u0014)d33)":[1705660862319921361,[0.70245653,[{},false,-4997087011668413281,0.70262194]]],"T":0.22172987,"\u001e":{"q":null,"\ncW\u0016&f":0.66210586}}
{"X$N":{},"":{",jkv\u00058":5185843031279423760,"l#\u001bm\t\f":"","\u001biD5\u0006":-9351375925373681315,"z\u000f))\u001e@":"\u001b3\u001bH"},"` f":{"\u00112Kf\r":null,"\tg1":[[null,false],[{},{"":-10702680099945297361,"`\u001ci.[":{"$@^k":{},"":697936244851400800,"6_z":"","RF\u0001v\u0005":false,"?":null},"@\t":"\u000b\u0005","":null},"@\u0017",-17783375405486191619],9136462765999751505],"i\u0016\"":10064203520377885526},"\bC":{"GG7c!":0.51944315,"m$g+\u0001\u0007":{"!\\B%!\u001a":"J\u000b\u0018","":[[0.5990416,16979263451032275490],[[-3319032353105603835]],0.8669717],"V\u001bZ9\u00078":{},"#}\u000e\u0015\u0010,":-11662685099556867956,"\bv":12939561448974367640},"\u000b\u0015@w_I":[[{"":{},"`5\u001a\u001c*H":{"XVcrv\u0012":"$",".`\u0010":1.0253668e-2,"&":-7969014040597734301,"Ae":0.68151945},"":{"q[_f+":{"W`$g":"\r&^\u0005","TjUUpg":"9"},"vE\u00115\fW":["&_",false,12477136470711026981]},"\u000f\r?\u0011{":15775777991788307493,"T\u001f6":0.36692625},{},{},{"\u001f:\u000eoH":{"VoZ\u001bP2":{"n\t\u0005>\u0011|":false,"G":[],"":"\u000b*!d","i8\u0003d":[],"X!N>":true}},"\u0014":0.7147376,"2<Z,":0.6847712,"=O":"\\J\u0015E","`\u001b[\u0016;k":null}],-16943125239715684542]},"F&Ok:":{"I1@~V":false}}
[["+\u000eMq\u0015n","0-Z\u0011"],{},0.19048035,{}]
{"":0.17463982,"B}O\u0006h":"j\u0016\u0016;$","v":false,"{q\\":0.1645158}
[[{},-10335304945929474818],"",[]]
[{"":11011572605828908966,"D\\y":8.9524806e-2},0.12063557,null]
[[{"[ F":[],"\fZ\u001a+#\u0012":0.20774364,"@u":14140938735167775918,"x\\":[-11016694664242453733,true,0.7662049,null],"f&\u0012":10041475338386091190}],[""]]
[{"p\u0006b\u001cK":null},{"":[[".\u0016\u000ex`j",-6879987747836866833,10223230372432915459,{"4O":1112402400137721488,"iCH\r":-11294462142224825198," k^":0.20589554},[8573083530212052064,"W37/d\t",""]],0.7530455]}]
[0.66232306]
{"G":null,"C":{")UT}N":0.5103738,"Jv\u001dy\u000bQ":"5H~3","\u001d~\u0016\u0015":true,"\u001f\u0001?(\u0017s":2473312665277409295,"|#m":false}}
[[{"_)":{"o":-12316865322655154751,"":{},"4rw-f":"c","\\J~":{"_nu\u000e":[9.66655e-2,-14195996720476945681],"I]\f~Ai":4.922563e-2," y":0.92399687,"T\u0005":null}}},{"6S^eEB":null},0.67890656,""],null,true,"\u000bx|",0.40480417]
[{"":"<\u001b\u001dNr"},3.2110214e-3,false,null,0.945265]
[{"y=b":{"57\u0006":{"3I":0.36540532,"R;*":"e0-R\u001e"}},"":{"\u001b6I\"":true,")t\u001e":false},"\u000e:~huo":0.90902025,"":[0.7344798,false]},[],[null,0.37931484]]
{"U":null,"='!n)]":null,"/B":4091446843733087751,"R(fu":{"xe>2":[["(",false,{"":0.42793614}]]},"-1\u0015z\u0005\u0011":false}
["y\u0007&I$",false]
[-13516489482431253430,"cVx"]
[{"\u0007\u0006g\u001c":-8554119748749916417},"\u001ch$SJ\u0014",{"Fh\u00050L7":[]},{"B":"\u0010T","\u0015>[c u":{"":"M3X"},"\u000f\u0004tFu~":0.36826915},-4617804547813974534]
{"\u00135_\u0014|y":13469420637207143766,"f01":0.38382834,"Zb":{"2z":true,"-oWAq":-17693981350083080169,"\u0015":-16142200838510853908},"":[-11453326168044549327,true,"W3Z+"]}
[-11153910131317761898,"\u001ch",[16314294140409942272,{},0.93278205,11399627290119531131],[]]
{"\u000e/\u0007Q\"\u0012":{"&b!0\f\u001e":null,"\u0016,C":10027704590075874049,"mRQ":"(\u000fb\u0019","":true},"\u001al1Tt":0.9605522,"":"+35Y","":12330351478004686954,"":{"":0.80495906,"E":0.68375826,"\fg":0.42994535}}
{"\u0010-~":[]}
{"O\u0010\u001fW":{"\t-cpRB":-1207104230604966545,"&#":false,"K<":{},",e":-3243935909847784433,"=\t\u00039$N":false},"K\"ZA":[false]}
[8330608987917035325,18104787239307741033]
{"8!F~}L":"kvA|w\u0017","{":{}}
{"]j\u0013]":13135044173561850680}
{"[":-14572228672651219669,"":null,"\"\b\u001a\n\u0017":[0.92823654,{"-z\u0010\u001f\u0004":7090716430674012613,"\u001dH8t":"","":-12005360874135090142,"\u0015K\u0014,{>":["\u0004DUO\u0012"]},"\bo",[0.19997853,false,0.44237286]],"-\u001dd":[-10911830261830972153,{"y\u000e'EI\u0004":"-vu","=$1":{"6-)`\r":"&J","\u001a<)\u0015V":{},"m\u001d":"|M"},"je\u0006":[],"\u0005BuM\u0019\n":{"":1782142678781591223,"8`\u0005\u001c\u0017":false,"b":"@4"},"\u0016m":"H"},{"E":17391435708081682382,"VH\u001b!":[-9580877567575004549],"Q\u000f":[5371389913356269173],"Aq :":"it"},-15750828222741139551]}
["$\u0005",0.3907426]
[8679419118201241337,0.19923568,0.668362,[null,0.44885856,-6773739461166169741,true],[]]
[0.4405555,{"":true,"\u0002&6O":-5924290840962216166,"\r\u0017\u0014C":4.2134523e-3}]
[0.9339055,""]
{"\u000f_":{"|^&m":[],"e\u0004z":{"\u0006+":true,"fb":"<W","<\u00184\u0019":"v\u001aZ","\bP\u0003\u001e":null,"\u0019":"l"},"\u001f":"Nr<yY"},"":"H\u001c"}
{"":"DO"}
[17256012902499945094]
{"\tR7":{"l\u000e":[],"\t\u0013Rx\u0011h":null,"wP":[14547570528401522133,14415130777834825591,{"":[3.6435902e-2]},"S\"g"],"\r\u001e\u0017":"1Tb#\u0016","\u001c1[\"":16344739751689581814}}
{" \u0002\bi":false,"Jh":[[0.33667308,-17369336385787834625,"\u0003ar","(\u0012!",[[true]]],-1396302974865016530,6.0578644e-2],"":"d\u0003j\u000f"}
{"":-8991839176212064162,"\u000f":0.87215024,"99q\u001b\u0013,":10827064753934197247,"$v7;":null,"\u0017pb":false}
{"-TgWQ":0.34665275,"J":{"dp":-9189006637028301388,"-":[{"\u0014\u001a\u0013":null},0.5770299]},"aC\b\u0016\u0001":{"V8\u001cz":{"w5*+9":false,"DV+Trn":{"ss":true}},"_Ln\u00025`":[0.91180235],"[\u0003~\f":4416088078877825503,"3\u001a_$0h":-8749706514808019600},"":"","2n*0#'":5896380210604322024}
{"jVZf.\u001d":[4570204019026784438,null,[],["9",null,{"A\u0005ZI":13260052419541173374,"LdW^O\u0002":true,"FeIw&":[0.89395565,{"'":{},"":[true],"\f\t_":672387497269769344,"\u0010Sr":-2157951215979429253},null,[[false,0.80883986,[],"Q"]],0.9129004],"\u0017\u0019XT":0.57672817,"r":"\u001a"},"",[true,[{"*Cmd~":-4798229312441466626,"pX#'nY":false,".\u0015i$U":[true,-14938229482389367753,true]},{"aF;_":0.79151475,"\u0012wOX\u0006}":"\u001cx2\u000f","UmK":1736833851845507255,"":{},"gtA7C":"\u0017\b\u0002"},[]],"`6\"9[;",-6877653363415966808,"\n"]],[[["?\u0010Xv",17795186868318363551,false,-11569501727506071697],[null],0.7076068,";2S","yTgS"],["T\b",11406439396583882123],null]],"\u000b{\bYJ":[[],{"=@Y(D":[-2689466064178754073,-4610455999244253621],"\u001c`6od":4005483234178880618},{"LXWddO":0.34575886,"t>y(":true},3.3874035e-2],"\u000b\u0010*=f":"/7\u00142-i","hm\u001c\t\nK":true,"j8dh\u0003g":{"e@ ":9390488342921961408,"'L{;":15069019766623989691,"U\r'I\u00135":-1903112358189338839}}
[{"~j":"\"0","=r\u0003":"*+r@0s","":0.19081467},".h\u0019}R"]
{"":"ju","m^~":null}
[[0.3681597],"","9\u001ad","e\u000fw"]
{"\u0015~e\u001bi%":"Kk\u0018"}
{"Y":{"r\u0005L\u001c":null,"4o8":0.15798777},"@8":4998012367855424481,"z":0.3768953,"0\u0012":{"/#E=,:":"","":0.7964526,"=I\u0012q\u0012":{"":6924435281412963366,"gf":[-4456579596822184692,"\u001fT~a"]}},"#)t":"f "}
{"T/({J":"GAGU,","T:":9.503329e-2}
[true,{"\u0011":-12217948383421578866,"7mw;m":true,"\u001aX&":0.5159844},"1",[[[0.29598004],[true,-15095167341672935746,[null,[[["W"],13939264792053538017,false,{"kKox;":4.7398925e-2,"Y8\u0013":[]}],[{"? ":"","\u0015":"7u"},0.35944933,-17901161567319789421,null],{"fB\u0011{\\O":null,"":0.8980226,"\u001fy":0.76103854,"G&e]":"\u0005KKQ]"},true],-16258605383369635883,null],[[],0.6391701,"\u0003R\u000f[u\u001f",12642424132404311926,["\u0016:z\u0004dk",[0.96023333,{"v1w":7.775545e-3,"7/\u0012\u0007k":{"n\u0010!":2886994691609596810},"(":8669124115758688723,"T":5.8164835e-2,"":""},{"0:\t":null,"\u000ev\u000eC":6.865555e-2,"|":1458938999225059326}]]],[{"S":[],"}x6":[]},true,-7723708214213539854,0.22195089,"\u0014\u0011\u0003$"]],{"[s":null},8786201060069342327],[0.90378374,"]*",{"+:":"k/\\","*s1\n\u0012":{"":[false,true,0.614962],"VS\n":-12408716702998572025,"\u0003H":-3423065839382307351,"9":["@}X\u000e",false,0.19913095,0.7669836,{"\u001e&|mM":"\u0014UVJe"}],"PbulK":0.79707235},"P":4909128968987850267,"":{"\u0017t":{},"":-13059659663321679233,"{\u001e)y#\u0018":{"7\"}&":false,"":["#25v",{},[null,0.749951,[")%\u001c2}",{"T":[{},1704987698030816733,14422845730038874925,0.2691697],"_\nq:":[[406467608425265142],{"":{"\u0017":{"x\u0010":[],"B":"@'QQc"},"\r6$\u0015Q":"\u0004","QQ":"<-\u0007=-","\u0002l/\u0001j":false},"":false},null,0.5161205,0.35366076]},"|\u001e","*3G^",false],null]]},"\u0017In":"xd"}}]],-11794737418073577276]
[-3875596124597186130,["h\u0017C \u0017-",0.51341975,[0.8909479,null,{"mLt$i":5931259876451380254,"q],\nTY":2747696704935867519,"xM\u0005":true,"\u001fON":-18301498459940935941}],0.8663333],{"GgR":{"oLH":12686260561828831420}},false,{"7":[[{}],null,true,5723369010967295510]}]
{"":"h@?4~_"}
{"\u001cO2":null,"ug`N":0.25825077}
{":zO":-17886397144894567202}
[false,{")":0.46914285,"":[null,17701263076267415360,"\u001b`",["<z\u00163",[0.6074513,{"":0.9845493,"\u0019l>\b":0.7487198,"yq8\u0018\u0013":0.48864508,"'vz^":["\u0016*c\u0019",true,null,[-16625719407348066219,0.27814394,[[]],-18146293756557779016,0.27746165],true]},["7z+","3qP%Y",0.8580701,-17461057325880571999,""],[[0.6738269],{"J-\u001d[":["\u00020h\u0007mf","b\u000bE\u0005\u0002\u001b",null,5861749072356890930,-6091629617043366513]},[[],2.647984e-2,0.665144,"]"]]]]],"^jQ\u0014$F":0.87910104,"Y":0.64870834},0.5446196,{"\u001c\u000f\u000bBI":false,"'U":null,"1\u0012=&":[["",true],"\u001btQ",true,"h"],"l2>":[10045547607866373384,0.12400788]},null]
[{"\u0018":-9879941084256315652,"":0.5923274,"\\%\b:":16734910719493405340,"Sff":" F;"}]
{"\u0018]":"\u000fS","0v":null,"":{"hqO":{"+C\u0011]":-10606520924323826758,"":["#X\\u",true],"Awxy\u0018":null,"Q+f\u0019BV":null,"H{y":0.45539778},"JRL~":"i\u001b&1","":-12146818804840233073,"7l\u0002|Y":{}}}
["\u0003"]
{"l0M":{"":-15071382070707286465,"\r{kC":4969650560638343485,"P9":"1","x\t@\u001d0":14144403874232608022," Ar{j":["U\u000eM",["",{"!yEB!":{"":[],"E":0.24353248,"p\t":"08\u0012"},";+":{"\u0005V\u0013\t\t":null,"":[null,[{"":[[[-6211830736174882578,{"&E\u001bQ":false,"C":0.9126065,"^K\u001e\u0005":[false],"mk1\u001d<Y":0.63055354,"\u0010":-12060799814929459901},0.16415578]],"*"],"":2607220307768334,"":null,"`@\u000b\u001b\u000b":11682738680454146932},["v\u0012!zI",false,null,null],true,"\u00183",["!\u0011Dy<p",0.6951675]],1.2881577e-2,9.184706e-2,[null,["/#6:%"],"\u0011.jR[y","K;\u0002nfq",null]]},"p~\u0015":11565752727145742707,"#.\rIy":14150557114163512681},true],"T:D\u0015",{"FbHagB":[{"r\u001e^":0.9365841,"k9`q":{},"ia9":-6722268436164996956,"q\u0014JV5T":-1173478293511733138,"\u001cD*{>h":0.67198795},-4756454757795884285,{"|6C":{"\u0005":"\u001a","\"?oHZ":true,"J!0T(":-3315779624432601658},"_@;":true,"{":null,"/WV9":false},{"'Z":true,"x\u0001\u0001K^\u0013":{"<\u001c\u0003":"g\u001cF\u0010\u001ex","/":-9581477031750821867,"kA\u0007":[],"":false,"XT\"h":0.9187148}},-4712406852286132207],"n\u001b%\\@:":true}]}}
{"0":{"^":false,"\u001d[Y":true,"\u00016\u0011%":{"^":-13422956777875372902,"":0.6748177,"UK":-14185007865157151724,"#":[]},"lV\f":16496414788763842682,"\\1hs":0.75634325},"":"J$cjy"}
{"j\u0018L\u0010":null,"~C":0.5380389,"wWfI\u001d":7214825108176793749,"\u0002*k":false}
[[0.7889624,0.19233567,{"B>\u0007_YS":0.54761636,"\u0015\u000f4\\\u0003":0.68591905,"Pi":-1082999137390589628},2313694156741794768,[[{"":-10441029711331055060,"":null},0.2104497,0.91654104,[true,"\u0006\u001eIN~M",2.5443256e-2,true,"Ybj"]],true,"\u0013"]],0.686076,[[null,0.8077588,{",\b)":{"x\u0012\u0002*',":false,"":{},"\u0014":[],"\u000b\u0002\u001d<":15337849397348555006},"\u00151It":[0.8737354,"R\u001fP",{"\u0005\u000eYd":"b\\*",",\u001c\u000b":0.1559087,",\u001c[":"",">":true,"":[0.6415,3655223684131779968,{"\u0012-\u001eyh":"Y"}]},[false,true,"8H\u0007\u0013"],{"`\u0006}MN":false,"\u0014\u001fG-}":{"":"~+\u0010","\u001a@:E\u0016":true,"FV\u00173":0.83031607,"i":{}},"'":{"":0.2809633}}]}]]]
{"F":[0.28045475,6395486746026534910],"":[],"=\n&@?%":0.75717854}
[[""],[-4429524012470214935,-2796661001313312842]]
[false]
["O\u0011ak!f",0.31498963,false,[true]]
{"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo":"baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar"}
{"vh#wH":null,"*l":2296691638,"W\u0002":3606753554,"`\u000e.\b":0.385041,"N3h4m":null}
{"M\u00185-":[0.86986375,0.33169776,"\u0015",true,-3787920576],"":-2266678859,"":{"\u001d":3294973006,"x(\u001d|":0.41438067,"#q":true,"":null},"L$":[false],"op[bi":null}
["'{\\y[","qK",2505258107]
{"\u0014\u0016":true,"7\u0013":true,"":[null]}
[null,[0.75785536],""]
{":":0.35312474,"*\nk":"?\u0014N)"}
{"":0.50028557,"P\u0005)&\u0002":null,"=\u0002y0":-2062734622}
[0.30801737,-2716449190,"6$am\u0002m",1.2181699e-2]
[{"\u0007khaUg":1401686211,"\bB":true,"":{}},[[{"\u0015IE[":"\u0012T"},"mE","f",0.94850457],"+zRXV"],772415052,{"\u00182\u001b\t":{".\u001bw\u0017s7":{"\u0010":[0.44792414],"o$|*":true}},"\r\u000flp\u001fF":{"D',.\fr":"(\"WSmP","c":[0.8529478],"C\n\u000bd\u001d":{"":-1054348074,"18rf\u001a":0.6809252},",g\u000e}":{}}}]
[0.9796113]
[0.6591127,[""],[0.79424393],{"XE\u001ep":{".(\u0016":false},"Z?lR\u0015,":"","\nz":[false,3117789160,false],"sd#P\u0004":2807155556,"\u0010":null},-1784589105]
{"+YJ(":null,"Aq#^_$":-2277615052,"":0.97045594}
["[\u001e*Pdj",null]
{"<\u000f\u001cE\u0010\t":89655629,"`x\u0004\u0015\u0005H":0.13868803,"\fb{)Z\u0004":-617492212,"We\u000f\u0005z\u0001":[0.75462246,"",0.7799082,409262730],"QfQ\u0017":0.8736933}
["\th/y\"",0.6817276]
{")k":[0.38475662,45616771,{"\u0002p\u0002{E":true,"O":596477863,"KoSE/C":null},0.9854614],"":-2886538406,"D+[R":"Yw\u000b{\u0015"}
[0.5344141,"P\u0017sJ\u00072",0.71697176,3692993782]
[{"zS{!":{"c\f,M6H":"A","5dba":{"-5\u0011\"":{},"i":2873171131,"\u0005NL":{}},"),h\u0005\bm":{"<|7T\n":null}},"\u0010":{"}\"Q<m":null,"\u001d\fT2I\u0012":{"":[],"":435778865},"/]":0.29311603,"p\u000b\b":"\u0004t","k":"xFe1"},"\u000f\u0007V":4152148563,"\u000eF%":-1627699888}]
{"a\u0013\fE":false,"\u000e+*d\u000e":605395782,"/\u0004q\u0014\"":"Q:\u0016\u001f","x$fJ":{}}
[["9"],true,0.43510032,{"`AzY":{"":false,"zv":null,"mRI\u0011":[true,[[3883676960]]],"DU\u0002":"SCC~T"}}]
[[-3290883344,0.54075867,0.7522493,null,null],"\r\u000f\u0015[",2429340856]
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":{"i":{"j":{"k":{"l":{"m":{"n":{"o":{"p":{"q":{"r":{"s":{"t":{"u":{"v":{"w":{"x":{"y":{"z":{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":{"i":{"j":{"k":{"l":{"m":{"n":{"o":{"p":{"q":{"r":{"s":{"t":{"u":{"v":{"w":{"x":{"y":{"z":{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":{"i":{"j":{"k":{"l":{"m":{"n":{"o":{"p":{"q":{"r":{"s":{"t":{"u":{"v":{"w":{"x":{"y":{"z":{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":{"i":{"j":{"k":{"l":{"m":{"n":{"o":{"p":{"q":{"r":{"s":{"t":{"u":{"v":{"w":{"x":{"y":{"z":{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":{"i":{"j":{"k":{"l":{"m":{"n":{"o":{"p":{"q":{"r":{"s":{"t":{"u":{"v":{"w":{"x":{"y":{"z":{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":{"i":{"j":{"k":{"l":{"m":{"n":{"o":{"p":{"q":{"r":{"s":{"t":{"u":{"v":{"w":{"x":{"y":{"z":{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":{"i":{"j":{"k":{"l":{"m":{"n":{"o":{"p":{"q":{"r":{"s":{"t":{"u":{"v":{"w":{"x":{"y":{"z":{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":{"i":{"j":{"k":{"l":{"m":{"n":{"o":{"p":{"q":{"r":{"s":{"t":{"u":{"v":{"w":{"x":{"y":{"z":{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":{"i":{"j":{"k":{"l":{"m":{"n":{"o":{"p":true}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
================================================
FILE: tests/test-quote.c
================================================
/*
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.
*/
#include "json65-quote.h"
static void do_test (const char *s) {
fputc ('\"', stdout);
j65_print_escaped (s, stdout);
fputs ("\"\n", stdout);
}
int main (int argc, char **argv) {
do_test ("Hello, World!");
do_test ("Hello, World!\n");
do_test ("Hello,\r\nWorld!");
do_test ("Hello, \"World!\"");
do_test ("\aHello, World!");
do_test ("Backslash \\");
do_test ("Hello,\tWorld!");
do_test ("\001\002\003");
return 0;
}
================================================
FILE: tests/test-string.c
================================================
/*
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.
*/
#include <stdio.h>
#include <string.h>
#include "json65-string.h"
#define ITERATIONS 300
static j65_strings strs;
static const char *results[ITERATIONS];
static char buf1[10];
static char buf2[10];
static void print_bucket_usage (void) {
uint8_t *lo;
uint8_t *hi;
uint16_t i, used = 0;
lo = (uint8_t *) &strs;
hi = lo + 256;
for (i = 0 ; i < 256 ; i++) {
if (lo[i] || hi[i])
used++;
}
printf ("used %u/256 buckets\n", used);
}
int main (int argc, char **argv) {
uint16_t i;
const char *tmp;
j65_init_strings (&strs);
for (i = 0 ; i < ITERATIONS ; i++) {
snprintf (buf1, sizeof (buf1), "%u", i);
results[i] = j65_intern_string (&strs, buf1);
if (strcmp (buf1, results[i]) != 0) {
printf ("first loop: '%.20s' (%p) not equal to '%s' (%p)\n",
results[i], results[i],
buf1, buf1);
return 1;
}
}
for (i = 0 ; i < ITERATIONS ; i++) {
snprintf (buf2, sizeof (buf2), "%u", i);
tmp = j65_intern_string (&strs, buf2);
if (strcmp (buf2, tmp) != 0) {
printf ("second loop: '%s' not equal to '%s'\n", tmp, buf2);
return 1;
}
if (tmp != results[i]) {
printf ("For '%s', %p not equal to %p\n", buf2, tmp, results[i]);
return 1;
}
}
print_bucket_usage ();
j65_free_strings (&strs);
printf ("Success!\n");
return 0;
}
================================================
FILE: tests/test-tree.c
================================================
/*
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.
*/
#include <stdio.h>
#include <string.h>
#include "json65-tree.h"
static char buf[1024];
static j65_parser parser;
static j65_tree tree;
static const char infile[] = "test-tree.json";
static int do_test (size_t len) {
int8_t status;
j65_node *n;
uint8_t node_type;
const char *str;
uint32_t line_number, column_number;
j65_init_tree (&tree);
j65_init (&parser, &tree, j65_tree_callback, 255);
status = j65_parse (&parser, buf, len);
if (status != J65_DONE) {
fprintf (stderr, "j65_parse returned status %d\n", status);
return 1;
}
n = j65_find_key (&tree, tree.root, "color");
if (n == NULL) {
fprintf (stderr, "couldn't find color\n");
return 1;
}
n = n->child;
n = j65_find_key (&tree, n, "linearCutoff");
if (n == NULL) {
fprintf (stderr, "couldn't find linearCutoff\n");
return 1;
}
n = n->child;
node_type = n->node_type;
if (node_type != J65_NUMBER) {
fprintf (stderr, "%u != J65_NUMBER\n", node_type);
return 1;
}
str = n->string;
if (0 != strcmp (str, "0.0078125")) {
fprintf (stderr, "%s != 0.0078125\n", str);
return 1;
}
line_number = n->location.line_number + 1;
column_number = n->location.column_number;
if (line_number != 8) {
fprintf (stderr, "line number %lu != 8\n", line_number);
return 1;
}
if (column_number != 33) {
fprintf (stderr, "column number %lu != 33\n", column_number);
return 1;
}
if (n->location.line_offset != 135) {
fprintf (stderr, "line offset %lu != 135\n", n->location.line_offset);
return 1;
}
n = j65_find_key (&tree, tree.root, "banana");
if (n != NULL) {
fprintf (stderr, "found banana but shouldn't have\n");
return 1;
}
j65_free_tree (&tree);
return 0;
}
int main (int argc, char **argv) {
FILE *f;
int badness = 0;
size_t len;
f = fopen (infile, "r");
if (! f) {
fprintf (stderr, "Couldn't open file '%s' for reading\n", infile);
return 1;
}
len = fread (buf, 1, sizeof (buf), f);
if (ferror (f)) {
fprintf (stderr, "Couldn't read file '%s'\n", infile);
return 1;
}
fclose (f);
badness = do_test (len);
if (badness == 0)
fprintf (stderr, "Success!\n");
return badness;
}
================================================
FILE: tests/test-tree.json
================================================
{
"listen": ["127.0.0.1", 7890],
"verbose": true,
"color": {
"gamma": 2.5,
"whitepoint": [1.0, 1.0, 1.0],
"linearCutoff": 0.0078125
},
"devices": [
{
"type": "fadecandy",
"map": [
[ 0, 0, 0, 50, "grb" ],
[ 0, 50, 64, 50, "grb" ]
]
}
]
}
================================================
FILE: tests/test.c
================================================
/*
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.
*/
#include <stdio.h>
#include <string.h>
#include <json65.h>
static j65_parser parser;
static int passes, failures;
static char buf[80];
#define MAGIC 0x2badbeef
typedef struct {
int8_t ev;
int32_t integer;
const char *str;
uint32_t line_no;
uint8_t depth;
} event_check;
typedef struct {
uint32_t magic;
const event_check *events;
size_t len;
size_t pos;
} my_context;
static const event_check test00[] = {
{ J65_DONE, 0, "[] ", 0, 0 },
{ J65_START_ARRAY, 0, NULL, 0, 1 },
{ J65_END_ARRAY, 0, NULL, 0, 1 },
};
static const event_check test01[] = {
{ J65_DONE, 1, "{} ", 0, 0 },
{ J65_START_OBJ, 0, NULL, 0, 1 },
{ J65_END_OBJ, 0, NULL, 0, 1 },
};
static const event_check test02[] = {
{ J65_DONE, 2, "1234 ", 0, 0 },
{ J65_INTEGER, 1234, "1234", 0, 0 },
};
static const event_check test03[] = {
{ J65_DONE, 3, "-10000000 ", 0, 0 },
{ J65_INTEGER, -10000000, "-10000000", 0, 0 },
};
static const event_check test04[] = {
{ J65_DONE, 4, "1.5 ", 0, 0 },
{ J65_NUMBER, 0, "1.5", 0, 0 },
};
static const event_check test05[] = {
{ J65_DONE, 5, "1e-2 ", 0, 0 },
{ J65_NUMBER, 0, "1e-2", 0, 0 },
};
static const event_check test06[] = {
{ J65_DONE, 6, "\"Hello, World\" ", 0, 0 },
{ J65_STRING, 0, "Hello, World", 0, 0 },
};
static const event_check test07[] = {
{ J65_DONE, 7, "null ", 0, 0 },
{ J65_NULL, 0, NULL, 0, 0 },
};
static const event_check test08[] = {
{ J65_DONE, 8, "false ", 0, 0 },
{ J65_FALSE, 0, NULL, 0, 0 },
};
static const event_check test09[] = {
{ J65_DONE, 9, "true ", 0, 0 },
{ J65_TRUE, 0, NULL, 0, 0 },
};
static const event_check test10[] = {
{ J65_DONE, 10, "{\"foo\": 5} ", 0, 0 },
{ J65_START_OBJ, 0, NULL, 0, 1 },
{ J65_KEY, 0, "foo", 0, 1 },
{ J65_INTEGER, 5, "5", 0, 1 },
{ J65_END_OBJ, 0, NULL, 0, 1 },
};
static const event_check test11[] = {
{ J65_DONE, 11, "{\"foo\": \"bar\", \"baz\": [1, 2, 3]} ", 0, 0 },
{ J65_START_OBJ, 0, NULL, 0, 1 },
{ J65_KEY, 0, "foo", 0, 1 },
{ J65_STRING, 0, "bar", 0, 1 },
{ J65_KEY, 0, "baz", 0, 1 },
{ J65_START_ARRAY, 0, NULL, 0, 2 },
{ J65_INTEGER, 1, "1", 0, 2 },
{ J65_INTEGER, 2, "2", 0, 2 },
{ J65_INTEGER, 3, "3", 0, 2 },
{ J65_END_ARRAY, 0, NULL, 0, 2 },
{ J65_END_OBJ, 0, NULL, 0, 1 },
};
static const event_check test12[] = {
{ J65_WANT_MORE, 12, "[1, 2, 3", 0, 0 },
{ J65_START_ARRAY, 0, NULL, 0, 1 },
{ J65_INTEGER, 1, "1", 0, 1 },
{ J65_INTEGER, 2, "2", 0, 1 },
};
static const event_check test13[] = {
{ J65_DONE, 13, "\n\"slash \\/ tab \\t\"", 1, 0 },
{ J65_STRING, 0, "slash / tab \t", 1, 0 },
};
static const event_check test14[] = {
{ J65_DONE, 14, "\"slash \\/ backslash \\\\ tab \\t\"", 0, 0 },
{ J65_STRING, 0, "slash / backslash \\ tab \t", 0, 0 },
};
static const event_check test15[] = {
{ J65_DONE, 15, "\"have \\u0061 nice day\"", 0, 0 },
{ J65_STRING, 0, "have a nice day", 0, 0 },
};
static const event_check test16[] = {
{ J65_DONE, 16, "\"have \\u0061\\u0020nice day\"", 0, 0 },
{ J65_STRING, 0, "have a nice day", 0, 0 },
};
static const event_check test17[] = {
{ J65_DONE, 17, "\"have \\u0061\\\\nice day\"", 0, 0 },
{ J65_STRING, 0, "have a\\nice day", 0, 0 },
};
static const event_check test18[] = {
{ J65_DONE, 18, "\"this \\uD834\\uDD1E is a G clef\"", 0, 0 },
{ J65_STRING, 0, "this 𝄞 is a G clef", 0, 0 },
};
static const event_check test19[] = {
{ J65_DONE, 19, "\"\\u00a9 2018\"", 0, 0 },
{ J65_STRING, 0, "© 2018", 0, 0 },
};
static const event_check test20[] = {
{ J65_DONE, 20, "\"cents \\u00a2 Euros \\u20ac\"", 0, 0 },
{ J65_STRING, 0, "cents ¢ Euros €", 0, 0 },
};
static const event_check test21[] = {
{ J65_DONE, 21, "[\nnull\n,\nfalse\n,\ntrue\n]\n", 6, 0 },
{ J65_START_ARRAY, 0, NULL, 0, 1 },
{ J65_NULL, 0, NULL, 1, 1 },
{ J65_FALSE, 0, NULL, 3, 1 },
{ J65_TRUE, 0, NULL, 5, 1 },
{ J65_END_ARRAY, 0, NULL, 6, 1 },
};
static const event_check test22[] = {
{ J65_EXPECTED_STRING, 22, "{false: true} ", 0, 0 },
{ J65_START_OBJ, 0, NULL, 0, 1 },
};
static const event_check test23[] = {
{ J65_EXPECTED_COLON, 23, "{\"hello\", \"world\"} ", 0, 0 },
{ J65_START_OBJ, 0, NULL, 0, 1 },
{ J65_KEY, 0, "hello", 0, 1 },
};
static const event_check test24[] = {
{ J65_DONE, 24, "[\rnull\r,\rfalse\r,\rtrue\r]\r", 6, 0 },
{ J65_START_ARRAY, 0, NULL, 0, 1 },
{ J65_NULL, 0, NULL, 1, 1 },
{ J65_FALSE, 0, NULL, 3, 1 },
{ J65_TRUE, 0, NULL, 5, 1 },
{ J65_END_ARRAY, 0, NULL, 6, 1 },
};
static const event_check test25[] = {
{ J65_DONE, 25, "[\r\nnull\r\n,\r\nfalse\r\n,\r\ntrue\r\n]\r\n", 6, 0 },
{ J65_START_ARRAY, 0, NULL, 0, 1 },
{ J65_NULL, 0, NULL, 1, 1 },
{ J65_FALSE, 0, NULL, 3, 1 },
{ J65_TRUE, 0, NULL, 5, 1 },
{ J65_END_ARRAY, 0, NULL, 6, 1 },
};
static const event_check test26[] = {
{ J65_DONE, 26, "2147483647 ", 0, 0 },
{ J65_INTEGER, 2147483647, "2147483647", 0, 0 },
};
static const event_check test27[] = {
{ J65_DONE, 27, "-2147483647 ", 0, 0 },
{ J65_INTEGER, -2147483647, "-2147483647", 0, 0 },
};
static const event_check test28[] = {
{ J65_DONE, 28, "2147483648 ", 0, 0 },
{ J65_NUMBER, 0, "2147483648", 0, 0 },
};
static const event_check test29[] = {
{ J65_DONE, 29, "-2147483648 ", 0, 0 },
{ J65_INTEGER, -2147483648, "-2147483648", 0, 0 },
};
static const event_check test30[] = {
{ J65_DONE, 30, "-2147483649 ", 0, 0 },
{ J65_NUMBER, 0, "-2147483649", 0, 0 },
};
static const event_check test31[] = {
{ J65_DONE, 31, "-4294967296 ", 0, 0 },
{ J65_NUMBER, 0, "-4294967296", 0, 0 },
};
static const event_check test32[] = {
{ J65_DONE, 32, "4294967296 ", 0, 0 },
{ J65_NUMBER, 0, "4294967296", 0, 0 },
};
static const event_check test33[] = {
{ J65_DONE, 33, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ", 0, 0 },
{ J65_NUMBER, 0, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 0, 0 },
};
static const event_check test34[] = {
{ J65_DONE, 34, "-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ", 0, 0 },
{ J65_NUMBER, 0, "-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 0, 0 },
};
static const event_check test35[] = {
{ J65_STRING_TOO_LONG, 35, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ", 0, 0 },
};
static const event_check test36[] = {
{ J65_PARSE_ERROR, 36, "5-5 ", 0, 0 },
};
static const event_check test37[] = {
{ J65_ILLEGAL_CHAR, 37, "0x2000 ", 0, 0 },
{ J65_INTEGER, 0, "0", 0, 0 },
};
static const event_check test38[] = {
{ J65_ILLEGAL_CHAR, 38, "barf ", 0, 0 },
};
static const event_check test39[] = {
{ J65_PARSE_ERROR, 39, "nue ", 0, 0 },
};
static const event_check test40[] = {
{ J65_PARSE_ERROR, 40, "]", 0, 0 },
};
static const event_check test41[] = {
{ J65_ILLEGAL_ESCAPE, 41, "\"This is \\j not allowed\"", 0, 0 },
};
static const event_check test42[] = {
{ J65_ILLEGAL_ESCAPE, 42, "\"This is \\uucp not either\"", 0, 0 },
};
static const event_check test43[] = {
{ J65_ILLEGAL_ESCAPE, 43, "\"And this? \\u\"", 0, 0 },
};
static const event_check test44[] = {
{ J65_DONE, 44, "{\"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\": \"baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar\"}", 0, 0 },
{ J65_START_OBJ, 0, NULL, 0, 1 },
{ J65_KEY, 0, "foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", 0, 1 },
{ J65_STRING, 0, "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar", 0, 1 },
{ J65_END_OBJ, 0, NULL, 0, 1 },
};
static const event_check test45[] = {
{ J65_DONE, 45, "[[[[]]]]", 0, 0 },
{ J65_START_ARRAY, 0, NULL, 0, 1 },
{ J65_START_ARRAY, 0, NULL, 0, 2 },
{ J65_START_ARRAY, 0, NULL, 0, 3 },
{ J65_START_ARRAY, 0, NULL, 0, 4 },
{ J65_END_ARRAY, 0, NULL, 0, 4 },
{ J65_END_ARRAY, 0, NULL, 0, 3 },
{ J65_END_ARRAY, 0, NULL, 0, 2 },
{ J65_END_ARRAY, 0, NULL, 0, 1 },
};
static const event_check test46[] = {
{ J65_NESTING_TOO_DEEP, 46, "[[[[[]]]]]", 0, 0 },
{ J65_START_ARRAY, 0, NULL, 0, 1 },
{ J65_START_ARRAY, 0, NULL, 0, 2 },
{ J65_START_ARRAY, 0, NULL, 0, 3 },
{ J65_START_ARRAY, 0, NULL, 0, 4 },
};
static const event_check test47[] = {
{ J65_PARSE_ERROR, 47, "[\n \"an\",\n \"extra\",\n \"comma\",\n]", 4, 0 },
{ J65_START_ARRAY, 0, NULL, 0, 1 },
{ J65_STRING, 0, "an", 1, 1 },
{ J65_STRING, 0, "extra", 2, 1 },
{ J65_STRING, 0, "comma", 3, 1 },
};
static const event_check test48[] = {
{ J65_EXPECTED_STRING, 48, "{ \"extra\": \"comma\", }", 0, 0 },
{ J65_START_OBJ, 0, NULL, 0, 1 },
{ J65_KEY, 0, "extra", 0, 1 },
{ J65_STRING, 0, "comma", 0, 1 },
};
static const char *event_name (uint8_t event) {
switch (event) {
case J65_NULL : return "J65_NULL";
case J65_FALSE : return "J65_FALSE";
case J65_TRUE : return "J65_TRUE";
case J65_INTEGER : return "J65_INTEGER";
case J65_NUMBER : return "J65_NUMBER";
case J65_STRING : return "J65_STRING";
case J65_KEY : return "J65_KEY";
case J65_START_OBJ : return "J65_START_OBJ";
case J65_END_OBJ : return "J65_END_OBJ";
case J65_START_ARRAY : return "J65_START_ARRAY";
case J65_END_ARRAY : return "J65_END_ARRAY";
default: return "?";
}
}
static void print_pass (void) {
printf ("\033[32mPASS\033[0m\n");
passes++;
}
static void print_fail (void) {
printf ("\033[31mFAIL\033[0m\n");
failures++;
}
static int8_t callback (j65_parser *p, uint8_t event) {
my_context *ctx = (my_context *) j65_get_context(p);
const char *ename = event_name (event);
size_t pos = ctx->pos;
const event_check *ec;
int32_t i;
const char *str;
size_t len1, len2;
uint32_t line_no;
uint8_t depth;
if (ctx->magic != MAGIC) {
print_fail();
printf ("Got magic $%08lx but expected $%08lx\n", ctx->magic, MAGIC);
return J65_USER_ERROR;
}
if (pos >= ctx->len) {
print_fail();
printf ("Got extra event of type %s\n", ename);
return J65_USER_ERROR;
}
ec = ctx->events + pos;
if (ec->ev != event) {
print_fail();
printf ("[%u] Got %s but expected %s\n",
pos, ename, event_name(ec->ev));
return J65_USER_ERROR;
}
if (event == J65_INTEGER) {
i = j65_get_integer(p);
if (i != ec->integer) {
print_fail();
printf ("[%u] Got %ld but expected %ld\n", pos, i, ec->integer);
return J65_USER_ERROR;
}
}
if (event == J65_INTEGER || event == J65_NUMBER ||
event == J65_STRING || event == J65_KEY) {
str = j65_get_string(p);
len1 = j65_get_length(p);
len2 = strlen (str);
if (len1 != len2) {
print_fail();
printf ("[%u] String length is %u but claimed to be %u\n",
pos, len2, len1);
return J65_USER_ERROR;
}
if (strcmp (str, ec->str) != 0) {
print_fail();
printf ("[%u] For %s, got '%s' but expected '%s'\n",
pos, ename, str, ec->str);
return J65_USER_ERROR;
}
}
line_no = j65_get_line_number(p);
if (line_no != ec->line_no) {
print_fail();
printf ("[%u] For %s, got line %lu but expected %lu\n",
pos, ename, line_no, ec->line_no);
return J65_USER_ERROR;
}
depth = j65_get_current_depth (p);
if (depth != ec->depth) {
print_fail ();
printf ("[%u] For %s, got depth %u but expected %u\n",
pos, ename, depth, ec->depth);
return J65_USER_ERROR;
}
ctx->pos++;
return 0;
}
static void run_test (const event_check *events, size_t len) {
my_context ctx;
uint32_t line_no;
int8_t ret;
const char *str = events->str;
printf ("test %02ld: ", events->integer);
ctx.magic = MAGIC;
ctx.events = events;
ctx.len = len;
ctx.pos = 1;
/* Use a small nesting depth to make it easy to test. */
j65_init (&parser, (void *) &ctx, callback, 4);
ret = j65_parse(&parser, str, strlen(str));
if (ret == J65_USER_ERROR) {
return;
}
if (ret != events->ev) {
print_fail();
printf ("Got return code %d but expected %d\n", ret, events->ev);
return;
}
if (ctx.pos != ctx.len) {
print_fail();
printf ("Got %u events but expected %u\n", ctx.pos - 1, ctx.len - 1);
return;
}
line_no = j65_get_line_number(&parser);
if (line_no != events->line_no) {
print_fail();
printf ("Final line number was %lu but expected %lu\n",
line_no, events->line_no);
return;
}
print_pass();
}
static void depth_test (uint8_t specified, uint8_t expected) {
uint8_t actual;
snprintf (buf, sizeof (buf), "depth test (%u):", specified);
printf ("%-18s", buf);
j65_init (&parser, NULL, NULL, specified);
actual = j65_get_max_depth (&parser);
if (actual != expected) {
print_fail ();
printf ("Got %u but expected %u\n", actual, expected);
} else {
print_pass ();
}
}
#define TEST(x) run_test (x, sizeof(x) / sizeof(x[0]))
int main (int argc, char **argv) {
int color;
TEST(test00);
TEST(test01);
TEST(test02);
TEST(test03);
TEST(test04);
TEST(test05);
TEST(test06);
TEST(test07);
TEST(test08);
TEST(test09);
TEST(test10);
TEST(test11);
TEST(test12);
TEST(test13);
TEST(test14);
TEST(test15);
TEST(test16);
TEST(test17);
TEST(test18);
TEST(test19);
TEST(test20);
TEST(test21);
TEST(test22);
TEST(test23);
TEST(test24);
TEST(test25);
TEST(test26);
TEST(test27);
TEST(test28);
TEST(test29);
TEST(test30);
TEST(test31);
TEST(test32);
TEST(test33);
TEST(test34);
TEST(test35);
TEST(test36);
TEST(test37);
TEST(test38);
TEST(test39);
TEST(test40);
TEST(test41);
TEST(test42);
TEST(test43);
TEST(test44);
TEST(test45);
TEST(test46);
TEST(test47);
TEST(test48);
depth_test (0, 224);
depth_test (1, 1);
depth_test (16, 16);
depth_test (123, 123);
depth_test (224, 224);
depth_test (255, 224);
if (failures > 0)
color = 31; /* red */
else
color = 32; /* green */
printf ("\033[%dm================================\033[0m\n", color);
printf ("%d tests passed; %d tests failed\n", passes, failures);
printf ("\033[%dm================================\033[0m\n", color);
return failures;
}
================================================
FILE: tools/README.md
================================================
This directory contains some scripts I used when making JSON65.
You probably won't need them.
================================================
FILE: tools/asm-heading.hs
================================================
#!/usr/bin/env stack
-- stack --resolver lts-12.6 --install-ghc runghc --package text
{-# LANGUAGE OverloadedStrings #-}
{-
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.
-}
import Control.Monad
import qualified Data.Text as T
import qualified Data.Text.IO as T
import System.Environment
width :: Int
width = 70
main = do
args <- getArgs
forM_ args $ \arg -> do
let title = T.pack arg
semis = T.replicate width ";"
T.putStrLn semis
T.putStrLn $ ";;" <> T.center (width - 4) ' ' title <> ";;"
T.putStrLn semis
T.putStrLn ""
================================================
FILE: tools/check-endproc.pl
================================================
#!/usr/bin/perl -w
# Script to check that the comment on the ".endproc" line matches
# the name on the corresponding ".proc" line.
use strict;
my $name = undef;
my $procLine = undef;
my $exitCode = 0;
while (<>) {
chomp;
if (/^\.proc\s+(\S+)/) {
$name = $1;
$procLine = $.;
} elsif (/^.endproc/) {
if (/;\s*(\S+)/) {
my $endName = $1;
if ($name ne $endName) {
print STDERR
"$ARGV: $endName on line $. but ",
"$name on line $procLine\n";
$exitCode = 1;
}
} else {
print STDERR
"$ARGV: no name on line $. but ",
"$name on line $procLine\n";
$exitCode = 1;
}
}
} continue {
close ARGV if eof; # Not eof()!
}
exit ($exitCode);
================================================
FILE: tools/convert_enums.pl
================================================
#!/usr/bin/perl -w
# 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.
my $inenum = 0;
sub hexify {
my $arg = $_[0];
$arg =~ s/0x/\$/;
$arg =~ s/(-\d+)/sprintf "\$%02x", $1 & 0xff/e;
return $arg;
}
while (<>) {
chomp;
if (/^enum\s+(\w+)/) {
print ";; $1\n";
print ".enum\n";
$inenum = 1;
} elsif (/^\}\;/) {
print ".endenum\n\n";
$inenum = 0;
} elsif ($inenum) {
s/^\s+(\w+\s*)(=?\s*[-\w]*),/"$1".hexify($2)/e;
s%/\*(.*)\*/%;$1%;
s/^\s*/ /;
s/\s+$//;
print $_, "\n";
}
}
================================================
FILE: tools/debug.inc
================================================
;; 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.
.import pusha0
.import pushax
.import _printf
zp_bytes = 24
.data
save0a: .byte 0
save0x: .byte 0
save_a: .byte 0
save_x: .byte 0
save_y: .byte 0
save_zp:
.res zp_bytes
.code
.macro save_regs
.local L
php
sta save_a
stx save_x
sty save_y
ldy #zp_bytes-1
L: lda sreg,y
sta save_zp,y
dey
bpl L
.endmacro ; save_regs
.macro restore_regs
.local L
ldy #zp_bytes-1
L: lda save_zp,y
sta sreg,y
dey
bpl L
ldy save_y
ldx save_x
lda save_a
plp
.endmacro ; restore_regs
.macro print_str str
.local S
.data
S: .asciiz str
.code
php
sta save0a
stx save0x
lda #<S
ldx #>S
jsr debug_str
ldx save0x
lda save0a
plp
.endmacro ; print_str
.macro print_hex
jsr debug_hex
.endmacro ; print_hex
.macro print_str_nl str
print_str str
jsr debug_nl
.endmacro ; print_str_nl
.macro print_str_hex str
print_str str
jsr debug_hex
.endmacro ; print_str_hex
.macro print_hex_nl
jsr debug_hex
jsr debug_nl
.endmacro ; print_hex_nl
.macro print_str_hex_nl str
print_str str
jsr debug_hex
jsr debug_nl
.endmacro ; print_str_hex_nl
.macro print_nl
jsr debug_nl
.endmacro ; print_nl
.macro print_long arg
save_regs
lda #<percent_08lx
ldx #>percent_08lx
jsr pushax
lda arg+2
sta sreg
lda arg+3
sta sreg+1
lda arg
ldx arg+1
jsr pusheax
ldy #6
jsr _printf
restore_regs
.endmacro ; print_log
.data
percent_08lx:
.asciiz "[%08lx]"
.code
.macro print_word arg
save_regs
lda #<percent_04x
ldx #>percent_04x
jsr pushax
lda arg
ldx arg+1
jsr pushax
ldy #4
jsr _printf
restore_regs
.endmacro ; print_log
.data
percent_04x:
.asciiz "[%04x]"
.code
.proc debug_str
save_regs
lda #<percent_s
ldx #>percent_s
jsr pushax
lda save_a
ldx save_x
jsr pushax
ldy #4
jsr _printf
restore_regs
rts
.data
percent_s:
.asciiz "%s"
.code
.endproc ; debug_str
.proc debug_hex
save_regs
lda #<percent_x
ldx #>percent_x
jsr pushax
lda save_a
jsr pusha0
ldy #4
jsr _printf
restore_regs
rts
.data
percent_x:
.asciiz "%02X"
.code
.endproc ; debug_hex
.proc debug_nl
save_regs
lda #<newline
ldx #>newline
jsr pushax
ldy #2
jsr _printf
restore_regs
rts
.data
newline:
.byte $0a, $00
.code
.endproc ; debug_nl
================================================
FILE: tools/make_charprops.pl
================================================
#!/usr/bin/perl -w
# 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.
my ($ws, $str, $lit, $int, $num) =
qw(prop_ws prop_str prop_lit prop_int prop_num);
my ($lsq, $lcur, $rsq, $rcur, $colon, $comma, $quote) =
qw(sc_lsq sc_lcur sc_rsq sc_rcur sc_colon sc_comma sc_quote);
my @props = ();
for (my $i = 0; $i < 128; $i++) {
push @props, [];
}
foreach my $c (0x20, 0x09, 0x0a, 0x0d) {
push $props[$c], $ws;
}
for (my $c = 32; $c < 128; $c++) {
push $props[$c], $str;
}
foreach my $c (split (//, "aeflnrstu")) {
push $props[ord($c)], $lit;
}
foreach my $c (split (//, "-0123456789")) {
push $props[ord($c)], $int;
push $props[ord($c)], $num;
}
foreach my $c (split (//, ".eE+")) {
push $props[ord($c)], $num;
}
push $props[ord("[")], $lsq;
push $props[ord("{")], $lcur;
push $props[ord("]")], $rsq;
push $props[ord("}")], $rcur;
push $props[ord(":")], $colon;
push $props[ord(",")], $comma;
push $props[ord("\"")], $quote;
print "charprops:\n";
my $c = 0;
foreach my $prop (@props) {
print " .byte ";
my $x = join ("|", @$prop
gitextract_3i7gmx_0/
├── .gitignore
├── LICENSE.txt
├── README.md
├── examples/
│ ├── example.c
│ └── input.json
├── run-tests.pl
├── src/
│ ├── json65-file.c
│ ├── json65-file.h
│ ├── json65-print.c
│ ├── json65-print.h
│ ├── json65-quote.h
│ ├── json65-quote.s
│ ├── json65-string.h
│ ├── json65-string.s
│ ├── json65-tree.c
│ ├── json65-tree.h
│ ├── json65.h
│ └── json65.s
├── tests/
│ ├── create-testfile-disk-image.pl
│ ├── file00.json
│ ├── file01.json
│ ├── file02.json
│ ├── file03.json
│ ├── file04.json
│ ├── file05.json
│ ├── file06.json
│ ├── file07.json
│ ├── test-file.c
│ ├── test-print.c
│ ├── test-print.json
│ ├── test-quote.c
│ ├── test-string.c
│ ├── test-tree.c
│ ├── test-tree.json
│ └── test.c
└── tools/
├── README.md
├── asm-heading.hs
├── check-endproc.pl
├── convert_enums.pl
├── debug.inc
├── make_charprops.pl
├── make_dispatch.pl
└── random-json.hs
SYMBOL INDEX (36 symbols across 13 files)
FILE: examples/example.c
function my_exit (line 39) | static void my_exit (int code) __attribute__ ((noreturn)) {
function main (line 47) | int main (int argc, char **argv) {
FILE: src/json65-file.c
function j65_parse_file (line 46) | int8_t __fastcall__ j65_parse_file (FILE *f,
function j65_default_err_func (line 157) | void __fastcall__ j65_default_err_func (FILE *err,
FILE: src/json65-print.c
function j65_print_tree (line 36) | int __fastcall__ j65_print_tree (j65_node *root, FILE *f) {
FILE: src/json65-string.h
type j65_strings (line 59) | typedef struct {
FILE: src/json65-tree.c
type j65_tree_internal (line 29) | typedef struct {
function j65_init_tree (line 36) | void __fastcall__ j65_init_tree (j65_tree *t) {
function j65_tree_callback (line 44) | int8_t __fastcall__ j65_tree_callback (j65_parser *p, uint8_t event) {
function j65_free_tree (line 158) | void __fastcall__ j65_free_tree (j65_tree *t) {
FILE: src/json65-tree.h
type j65_source_location (line 52) | typedef struct {
type j65_node (line 58) | typedef struct j65_node j65_node;
type j65_node (line 86) | struct j65_node {
type j65_tree (line 117) | typedef struct {
FILE: src/json65.h
type j65_event (line 37) | enum j65_event {
type j65_status (line 76) | enum j65_status {
type j65_parser (line 99) | typedef struct {
FILE: tests/test-file.c
function errfunc (line 32) | static void errfunc (FILE *err, void *ctx, int8_t status) {
function callback (line 36) | static int8_t callback (j65_parser *p, uint8_t event) {
function main (line 40) | int main (int argc, char **argv) {
FILE: tests/test-print.c
function do_test (line 35) | static int do_test (void) {
function main (line 88) | int main (int argc, char **argv) {
FILE: tests/test-quote.c
function do_test (line 27) | static void do_test (const char *s) {
function main (line 33) | int main (int argc, char **argv) {
FILE: tests/test-string.c
function print_bucket_usage (line 36) | static void print_bucket_usage (void) {
function main (line 52) | int main (int argc, char **argv) {
FILE: tests/test-tree.c
function do_test (line 34) | static int do_test (size_t len) {
function main (line 102) | int main (int argc, char **argv) {
FILE: tests/test.c
type event_check (line 35) | typedef struct {
type my_context (line 43) | typedef struct {
function print_pass (line 351) | static void print_pass (void) {
function print_fail (line 356) | static void print_fail (void) {
function callback (line 361) | static int8_t callback (j65_parser *p, uint8_t event) {
function run_test (line 444) | static void run_test (const event_check *events, size_t len) {
function depth_test (line 488) | static void depth_test (uint8_t specified, uint8_t expected) {
function main (line 507) | int main (int argc, char **argv) {
Condensed preview — 43 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (215K chars).
[
{
"path": ".gitignore",
"chars": 111,
"preview": "*~\n*.o\n*.map\n*.tmp\ntest\ntest-string\ntest-tree\ntest-quote\ntest-print\ntestfile.system\ntestfile.po\nexample.system\n"
},
{
"path": "LICENSE.txt",
"chars": 855,
"preview": "Copyright © 2018 Patrick Pelletier\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no e"
},
{
"path": "README.md",
"chars": 9357,
"preview": "I was watching TV, and there was a commercial which proclaimed, \"It's\ntime to do what *you* want!\" I replied to the TV,"
},
{
"path": "examples/example.c",
"chars": 4391,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "examples/input.json",
"chars": 147,
"preview": "{\n \"apple\": 2,\n \"banana\": {\n \"color\": \"yellow\",\n \"edible\": true,\n \"sieverts\": 9.82e-8\n },\n"
},
{
"path": "run-tests.pl",
"chars": 5832,
"preview": "#!/usr/bin/perl -w\n\n# JSON65 - A JSON parser for the 6502 microprocessor.\n#\n# https://github.com/ppelleti/json65\n#\n# Cop"
},
{
"path": "src/json65-file.c",
"chars": 5196,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "src/json65-file.h",
"chars": 5573,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "src/json65-print.c",
"chars": 4007,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "src/json65-print.h",
"chars": 1378,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "src/json65-quote.h",
"chars": 1377,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "src/json65-quote.s",
"chars": 5012,
"preview": ";; JSON65 - A JSON parser for the 6502 microprocessor.\n;;\n;; https://github.com/ppelleti/json65\n;;\n;; Copyright © 2018 P"
},
{
"path": "src/json65-string.h",
"chars": 3362,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "src/json65-string.s",
"chars": 6567,
"preview": ";; JSON65 - A JSON parser for the 6502 microprocessor.\n;;\n;; https://github.com/ppelleti/json65\n;;\n;; Copyright © 2018 P"
},
{
"path": "src/json65-tree.c",
"chars": 5268,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "src/json65-tree.h",
"chars": 5841,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "src/json65.h",
"chars": 11342,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "src/json65.s",
"chars": 47695,
"preview": ";; JSON65 - A JSON parser for the 6502 microprocessor.\n;;\n;; https://github.com/ppelleti/json65\n;;\n;; Copyright © 2018 P"
},
{
"path": "tests/create-testfile-disk-image.pl",
"chars": 2193,
"preview": "#!/usr/bin/perl -w\n\n# JSON65 - A JSON parser for the 6502 microprocessor.\n#\n# https://github.com/ppelleti/json65\n#\n# Cop"
},
{
"path": "tests/file00.json",
"chars": 21,
"preview": "{ \"hello\", \"error\" }\n"
},
{
"path": "tests/file01.json",
"chars": 23,
"preview": "{ \"mismatched\": true ]\n"
},
{
"path": "tests/file02.json",
"chars": 16,
"preview": "{ null: \"bad\" }\n"
},
{
"path": "tests/file03.json",
"chars": 18,
"preview": "{ \"error\": $foo }\n"
},
{
"path": "tests/file04.json",
"chars": 44,
"preview": "[[[[[[[[[[[[[[[[[\"nesting\"]]]]]]]]]]]]]]]]]\n"
},
{
"path": "tests/file05.json",
"chars": 57,
"preview": "{\n \"multiple\": 1,\n \"lines\": 2,\n \"trouble\":: 3\n}\n"
},
{
"path": "tests/file06.json",
"chars": 40,
"preview": "[\n \"an\",\n \"extra\",\n \"comma\",\n]\n"
},
{
"path": "tests/file07.json",
"chars": 263,
"preview": "[ \"a string which is way too long: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!0123456789abcdefghijk"
},
{
"path": "tests/test-file.c",
"chars": 1873,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "tests/test-print.c",
"chars": 2748,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "tests/test-print.json",
"chars": 27828,
"preview": "[\"Hello\",\"World!\"]\n{\"foo\":\"bar\",\"nested\":[1,2,3]}\n\"Just a string\"\n5\nfalse\n[0.32572436,true,-11186217319148845949,true,0."
},
{
"path": "tests/test-quote.c",
"chars": 1450,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "tests/test-string.c",
"chars": 2484,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "tests/test-tree.c",
"chars": 3400,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "tests/test-tree.json",
"chars": 373,
"preview": "{\n \"listen\": [\"127.0.0.1\", 7890],\n \"verbose\": true,\n\n \"color\": {\n \"gamma\": 2.5,\n \"whitepoint\": [1"
},
{
"path": "tests/test.c",
"chars": 18568,
"preview": "/*\n JSON65 - A JSON parser for the 6502 microprocessor.\n\n https://github.com/ppelleti/json65\n\n Copyright © 2018 Patri"
},
{
"path": "tools/README.md",
"chars": 94,
"preview": "This directory contains some scripts I used when making JSON65.\nYou probably won't need them.\n"
},
{
"path": "tools/asm-heading.hs",
"chars": 1489,
"preview": "#!/usr/bin/env stack\n-- stack --resolver lts-12.6 --install-ghc runghc --package text\n\n{-# LANGUAGE OverloadedStrings #-"
},
{
"path": "tools/check-endproc.pl",
"chars": 863,
"preview": "#!/usr/bin/perl -w\n\n# Script to check that the comment on the \".endproc\" line matches\n# the name on the corresponding \"."
},
{
"path": "tools/convert_enums.pl",
"chars": 1527,
"preview": "#!/usr/bin/perl -w\n\n# JSON65 - A JSON parser for the 6502 microprocessor.\n#\n# https://github.com/ppelleti/json65\n#\n# Cop"
},
{
"path": "tools/debug.inc",
"chars": 4207,
"preview": ";; JSON65 - A JSON parser for the 6502 microprocessor.\n;;\n;; https://github.com/ppelleti/json65\n;;\n;; Copyright © 2018 P"
},
{
"path": "tools/make_charprops.pl",
"chars": 2237,
"preview": "#!/usr/bin/perl -w\n\n# JSON65 - A JSON parser for the 6502 microprocessor.\n#\n# https://github.com/ppelleti/json65\n#\n# Cop"
},
{
"path": "tools/make_dispatch.pl",
"chars": 2939,
"preview": "#!/usr/bin/perl -w\n\n# JSON65 - A JSON parser for the 6502 microprocessor.\n#\n# https://github.com/ppelleti/json65\n#\n# Cop"
},
{
"path": "tools/random-json.hs",
"chars": 3347,
"preview": "#!/usr/bin/env stack\n-- stack --resolver lts-12.6 --install-ghc runghc --package MonadRandom\n\n{-\n JSON65 - A JSON parse"
}
]
About this extraction
This page contains the full source code of the ppelleti/json65 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 43 files (196.7 KB), approximately 60.3k tokens, and a symbol index with 36 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.