[
  {
    "path": ".gitignore",
    "content": "*~\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",
    "content": "Copyright © 2018 Patrick Pelletier\n\nThis software is provided 'as-is', without any express or implied\nwarranty.  In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n"
  },
  {
    "path": "README.md",
    "content": "I was watching TV, and there was a commercial which proclaimed, \"It's\ntime to do what *you* want!\"  I replied to the TV, \"It's time to write\na JSON parser in 6502 assembly language?\"  Somehow I don't think\nthat's what they had in mind, but the TV is right, I *should* do what\nI want.\n\nSo, here is my JSON parser.  The core parser is written entirely in\n6502 assembly language, and is meant to be assembled with [ca65][1].\nHowever, it is meant to be called from C, and uses the\n[cc65 calling convention][2] (specifically, the `fastcall` convention).\n\nJSON65 should work on any processor in the 6502 family.  (It does not\nuse any 65C02 instructions.)\n\nThe assembly language parts of JSON65 use the zero page locations used\nby `cc65`, in a way which is compatible with the C calling convention.\n\nJSON65 should work on any target supported by the `cc65` toolchain.  I\nhave tested it on `sim65` and on an unenhanced Apple //e.\n\n## Parser (json65.h)\n\nJSON65 is an event-driven (SAX-style) parser, so the parser is given a\ncallback function, which it calls for each event.\n\nJSON65 supports incremental parsing, so you can freely feed it any\nsized chunks of input, and you don't need to have the whole file in\nmemory at once.\n\nJSON65 is fully reentrant, so you can incrementally parse several\nfiles at once if you so desire.\n\nJSON65 does have a couple of limits: strings are limited to 255 bytes,\nand the nesting depth (of nested arrays or objects) is limited to 224.\nHowever, there is no limit on the length of a line, or the length of a\nfile.\n\nJSON65 uses 512 bytes of memory for each parser, which must be\nallocated by the caller.  JSON65 does not use dynamic memory\nallocation.\n\nIn accordance with the [JSON specification][3], JSON65 assumes its\ninput is UTF-8 encoded.  However JSON65 does not validate the UTF-8,\nso any encoding can be used, as long as all bytes with the high bit\nclear represent ASCII characters.  Bytes with the high bit set are\nonly allowed inside strings.  The only place where JSON65 assumes\nUTF-8 is in the processing of `\\u` escape sequences.  In accordance\nwith the JSON specification, a single `\\u` escape can be used to\nspecify code points in the Basic Multilingual Plane, and two\nconsecutive `\\u` escapes (a UTF-16 surrogate pair) can be used to\nspecify a code point outside the Basic Multilingual Plane.  These\nescapes will be translated into the proper UTF-8.\n\nBecause JSON only allows newlines in places where arbitrary whitespace\nis allowed, JSON65 is agnostic to the type of line ending.  (CR, LF,\nor CRLF.)  For the purposes of counting line numbers for error\nreporting, JSON65 handles CR, LF, or CRLF line endings.\n\nJSON65 will parse numbers which fit into a 32-bit signed long, and\nwill provide the long to the callback.  All other numbers\n(i. e. floating point numbers, or integers which overflow a 32-bit\nlong) are provided to the callback as a string.  (Like strings,\nnumbers cannot be more than 255 digits long.)\n\nThe callback function may return an error if it wishes.  This will\ncause parsing to stop immediately, and the error code returned by the\ncallback will be returned by `j65_parse()`.  Error codes are negative\nnumbers, and the user may use the codes from `J65_USER_ERROR` to\n`-1`, inclusive, for their own error codes.\n\n## Tree interface (json65-tree.h)\n\nIf you use the event-driver parser, you'll need to build your own data\nstructure (or otherwise handle the data somehow) as the events come\nin.  If you don't want to do that, you can use the tree interface\n(`json65-tree.h`) instead, which builds up a data structure for you.\nThis only works for small files, because the entire tree has to fit in\nmemory at once.\n\nUnlike the event-based parser, the tree interface uses dynamic memory\nallocation.\n\n## Printing JSON (json65-print.h)\n\nMostly, JSON65 is a parser.  However, it does have some support for\nprinting JSON back to a file, in `json65-print.h`.  The function\n`j65_print_tree()` will print a JSON tree (from the tree interface in\n`json65-tree.h`) to a given filehandle.  It prints the entire JSON\ntree on a single line with no whitespace.  This is the most compact\nformat for a machine-readable JSON file, but it is not particularly\nhuman-readable.\n\nIf you write your own code to print JSON, either because you want to\npretty-print it, or because you are using a data structure other than\n`j65_node`, you may still want to use the function\n`j65_print_escaped()` from `json65-quote.h`.  It handles escaping a\nstring using the JSON escape sequences.\n\n## API documentation\n\nI don't have any fancy Doxygen documentation, but the API is\ndocumented by comments in the header files.  If you wish to use the\nevent-driven parser, read [json65.h](src/json65.h).  If you wish to\nuse the tree interface, read [json65-tree.h](src/json65-tree.h).\n\n## Library organization\n\nIf you simply wish to use the event-driven (SAX-style) parser, you\nonly need one header file (`json65.h`) and one assembly file\n(`json65.s`).  However, there are some helper functions in other\nfiles, which you can optionally use with JSON65 if you like.  Most\nnotable is the tree interface to JSON65, which you may use instead of\nthe event-driven interface for small files.\n\nEach header file corresponds directly to one implementation file.\nSome of the implementation files are written in assembly language, and\nsome are written in C.  Here is a description of each, along with the\nsize of the machine code of the implementation (`CODE` section plus\n`RODATA` section; none of the implementation files have any `DATA` or\n`BSS`).\n\n* [json65.h](src/json65.h) (2240 bytes) - The core, event-driven\n  parser.  This is the only file that is required if you wish to build\n  your own data structure.\n* [json65-string.h](src/json65-string.h) (291 bytes) - This implements\n  a [string intern pool][4] which is used by the tree interface.\n* [json65-tree.h](src/json65-tree.h) (1300 bytes) - The tree\n  interface, which builds up a tree data structure as the file is\n  parsed.  You may then traverse the tree to your heart's content.\n* [json65-quote.h](src/json65-quote.h) (226 bytes) - This has a\n  function which prints strings, replacing special characters with the\n  escape sequences from the JSON specification.  It is used by the\n  tree printer, but can also be used standalone if you are printing\n  JSON files yourself without using the tree interface.\n* [json65-print.h](src/json65-print.h) (710 bytes) - Prints a tree to\n  a file as JSON.  Use this if you are using the tree interface, and\n  wish to write JSON files as well as read them.\n* [json65-file.h](src/json65-file.h) (1378 bytes) - Provides a helper\n  function to feed data to the parser from a file, in chunks, and to\n  display error messages to the user (including printing the offending\n  line, and printing a caret to indicate the offending position of the\n  line).\n\nI hate build systems (or at least, build systems for C code), so I\nhave not provided one.  (Other than a lame little Perl script to build\nand run the tests using [sim65][5].)  Instead, I encourage you to copy\nthe source files and header files you need into your own project, and\nuse whatever build system you are already using for your project.\n(Such as the GNU Make based [cc65 build system][6].)\n\nYou can use the following dependency graph to determine which source\nfiles you will need to copy into your project.  (For each source file,\nyou will also need to copy the corresponding header file.)  Source\nfiles with no dependencies (such as `json65.s`) are at the top of the\ngraph, while the source file with the most dependencies\n(`json65-print.c`) is at the bottom of the graph.\n\n```\n                json65.s    json65-string.s\n                  /  \\         /\n                 /    \\       /\n                /      \\     /\n     json65-file.c    json65-tree.c     json65-quote.s\n                           \\             /\n                            \\           /\n                             \\         /\n                            json65-print.c\n```\n\nIf you wish to build and run the tests, simply run the `run-test.pl`\nPerl script at the top level of the repository.  (It takes no\narguments.)  You'll need to have the [cc65][7] toolchain installed.\n\nNote: version 2.17 and earlier of [sim65][5] have a\n[bug in the implementation of the BIT instruction][8], so the tests\nwill fail.  You'll need a more recent version to get the tests to\npass.  (This only affects the simulation of the tests.  If you plan on\nrunning JSON65 on real hardware, or on an emulator *other* than sim65,\nthen you'll be fine with an older version of cc65.)\n\n## License\n\nJSON65 is licensed under the [zlib/libpng license](LICENSE.txt), which\nis approved by the [OSI][9] and the [FSF][10].\n\n## Background\n\nFor more information about how and why I wrote JSON65, see [my blog post][11].\n\n[1]: https://cc65.github.io/doc/ca65.html\n[2]: https://cc65.github.io/doc/cc65-intern.html\n[3]: https://tools.ietf.org/html/rfc8259#section-8.1\n[4]: https://en.wikipedia.org/wiki/String_interning\n[5]: https://cc65.github.io/doc/sim65.html\n[6]: https://github.com/cc65/wiki/wiki/Bigger-Projects\n[7]: https://cc65.github.io/cc65/\n[8]: https://github.com/cc65/cc65/pull/712\n[9]: https://opensource.org/licenses/Zlib\n[10]: https://www.gnu.org/licenses/license-list.en.html#ZLib\n[11]: https://funwithsoftware.org/posts/2021-03-03-json-on-the-apple-ii.html\n"
  },
  {
    "path": "examples/example.c",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#include <stdbool.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <conio.h>\n#include <cc65.h>\n\n#include \"json65.h\"\n#include \"json65-file.h\"\n#include \"json65-tree.h\"\n#include \"json65-print.h\"\n\nstatic uint8_t scratch[2048];\nstatic j65_tree tree;\n\nstatic void my_exit (int code) __attribute__ ((noreturn)) {\n    if (doesclrscrafterexit ()) {\n        cursor (true);\n        cgetc ();\n    }\n    exit (code);\n}\n\nint main (int argc, char **argv) {\n    const char *filename;\n    FILE *f;\n    int8_t ret;\n    j65_node *banana, *banana_value;\n    uint8_t width, height;\n\n    if (argc >= 2) {\n        filename = argv[1];\n    } else {\n        filename = \"input.json\";\n    }\n\n    /* Find out the width of the screen.  (This will be used\n    ** when formatting error messages.)\n    */\n    screensize (&width, &height);\n\n    /* Initialize the tree data structure. */\n    j65_init_tree (&tree);\n\n    /* Open the input file. */\n    f = fopen (filename, \"r\");\n    if (!f) {\n        perror (filename);\n        my_exit (EXIT_FAILURE);\n    }\n\n    /* Call j65_parse_file() to parse the file, and it will in turn\n    ** call j65_tree_callback() to build up the tree.\n    */\n    ret = j65_parse_file (f,                 /* file to parse */\n                          scratch,           /* pointer to a scratch buffer */\n                          sizeof (scratch),  /* length of scratch buffer */\n                          &tree,             /* \"context\" for callback */\n                          j65_tree_callback, /* the callback function */\n                          0,                 /* 0 means use max nesting depth */\n                          stderr,            /* where to print errors */\n                          width,             /* width of screen (for errors) */\n                          filename,          /* used in error messages */\n                          NULL);             /* no custom error func */\n    if (ret < 0) {\n        /* Don't need to print any error message here, because\n        ** j65_parse_file() already printed an error message before\n        ** returning a negative number.\n        */\n        fclose (f);\n        my_exit (EXIT_FAILURE);\n    }\n\n    /* We're done reading the file, so we can close it now. */\n    fclose (f);\n\n    /* Look for a key named \"banana\". */\n    banana = j65_find_key (&tree, tree.root, \"banana\");\n    if (banana == NULL) {\n        printf (\"Could not find banana.\\n\");\n        j65_free_tree (&tree);\n        my_exit (EXIT_FAILURE);\n    }\n\n    /* The variable \"banana\" now points to the key \"banana\" in\n    ** the tree.  If we want to know the value associated with\n    ** the key \"banana\", we need to look at the key's child node.\n    */\n    banana_value = banana->child;\n\n    /* Now print the value associated with the key \"banana\". */\n    printf (\"Here are some fun facts about bananas, from line %lu of %s:\\n\",\n            banana_value->location.line_number + 1, filename);\n    ret = j65_print_tree (banana_value, stdout);\n    if (ret < 0) {\n        perror (\"error printing tree\");\n        j65_free_tree (&tree);\n        my_exit (EXIT_FAILURE);\n    }\n\n    /* j65_print_tree() prints everything on one line (so, not\n    ** particularly human readable) without a newline at the\n    ** end, so we must print the newline ourselves.\n    */\n    printf (\"\\n\");\n\n    /* We are done, so we can free the tree now. */\n    j65_free_tree (&tree);\n\n    my_exit (EXIT_SUCCESS);\n}\n"
  },
  {
    "path": "examples/input.json",
    "content": "{\n    \"apple\": 2,\n    \"banana\": {\n        \"color\": \"yellow\",\n        \"edible\": true,\n        \"sieverts\": 9.82e-8\n    },\n    \"raspberry\": 3.14519\n}\n"
  },
  {
    "path": "run-tests.pl",
    "content": "#!/usr/bin/perl -w\n\n# JSON65 - A JSON parser for the 6502 microprocessor.\n#\n# https://github.com/ppelleti/json65\n#\n# Copyright © 2018 Patrick Pelletier\n#\n# This software is provided 'as-is', without any express or implied\n# warranty.  In no event will the authors be held liable for any damages\n# arising from the use of this software.\n#\n# Permission is granted to anyone to use this software for any purpose,\n# including commercial applications, and to alter it and redistribute it\n# freely, subject to the following restrictions:\n#\n# 1. The origin of this software must not be misrepresented; you must not\n#    claim that you wrote the original software. If you use this software\n#    in a product, an acknowledgment in the product documentation would be\n#    appreciated but is not required.\n# 2. Altered source versions must be plainly marked as such, and must not be\n#    misrepresented as being the original software.\n# 3. This notice may not be removed or altered from any source distribution.\n\nuse FindBin;\nmy $root = $FindBin::Bin;\n\nchdir ($root);\n\nmy $src = \"src\";\nmy $test = \"tests\";\nmy $example = \"examples\";\n\nmy $blue = \"\\e[34m\";\nmy $green = \"\\e[32m\";\nmy $red = \"\\e[31m\";\nmy $off = \"\\e[0m\";\n\nmy %test_results = ();\n\nmy $total_bytes = 0;\n\nsub mysystem {\n    my @cmd = @_;\n    print join(\" \", @cmd), \"\\n\";\n    if (system (@cmd) != 0) {\n        if ($? == -1) {\n            die \"$red*** fatal: $!$off\\n\";\n        } elsif ($? & 127) {\n            die (sprintf (\"$red*** fatal: signal %d$off\\n\", $? & 127));\n        } else {\n            die (sprintf (\"$red*** fatal: exit code %d$off\\n\", $? >> 8));\n        }\n    }\n}\n\nsub mysystem_nonfatal {\n    my @cmd = @_;\n    print join(\" \", @cmd), \"\\n\";\n    if (system (@cmd) != 0) {\n        if ($? == -1) {\n            die \"$red*** fatal: $!$off\\n\";\n        } elsif ($? & 127) {\n            die (sprintf (\"$red*** fatal: signal %d$off\\n\", $? & 127));\n        } else {\n            return \"fail\";\n        }\n    }\n    return \"pass\";\n}\n\nsub print_heading {\n    my $str = $_[0];\n    print \"$blue*** $str$off\\n\";\n}\n\nsub build_program {\n    my ($hash, @sources) = @_;\n\n    my $prog = $hash->{\"prog\"};\n    my $map = $prog . \".map\";\n    my $target = \"sim6502\";\n    $target = $hash->{\"target\"} if (exists $hash->{\"target\"});\n\n    my @cmd = (\"cl65\", \"-I$src\", \"-W-unused-param\", \"-O\");\n    push @cmd, '-t', $target;\n    push @cmd, '-C', $hash->{\"config\"} if (exists $hash->{\"config\"});\n    push @cmd, '-o', $prog;\n    push @cmd, '-m', $map;\n    push @cmd, @sources;\n\n    mysystem (@cmd);\n}\n\nsub run_test {\n    my $t = $_[0];\n\n    print_heading \"Running $t\";\n    $test_results{$t} = mysystem_nonfatal (\"sim65\", $t);\n}\n\nsub parse_map {\n    my $map = $_[0];\n    my $name = \"\";\n    my $size = 0;\n    my %result = ();\n\n    open F, $map or die;\n    while (<F>) {\n        chomp;\n        if (/^([^:\\s]+):$/) {\n            $name = $1;\n            $size = 0;\n        } elsif (/^\\s+[A-Z].* Size\\=(\\w+)/) {\n            my $sz = hex ($1);\n            $size += $sz;\n            $result{$name} = $size;\n        } elsif (/^$/) {\n            last;\n        }\n    }\n    close F;\n\n    return \\%result;\n}\n\nsub print_size {\n    my ($sizes, $obj) = @_;\n    my $size = $sizes->{$obj};\n\n    printf \"%-15s %4u bytes\\n\", $obj, $size;\n    $total_bytes += $size;\n}\n\nprint_heading \"Building tests\";\n\n# Tests which can be built for sim65\nbuild_program({'prog' => \"$test/test\"},\n              \"$src/json65.s\", \"$test/test.c\");\nbuild_program({'prog' => \"$test/test-string\"},\n              \"$src/json65-string.s\", \"$test/test-string.c\");\nbuild_program({'prog' => \"$test/test-tree\"},\n              \"$src/json65.s\", \"$src/json65-string.s\", \"$src/json65-tree.c\",\n              \"$test/test-tree.c\");\nbuild_program({'prog' => \"$test/test-quote\"},\n              \"$src/json65-quote.s\", \"$test/test-quote.c\");\nbuild_program({'prog' => \"$test/test-print\"},\n              \"$src/json65.s\", \"$src/json65-string.s\", \"$src/json65-tree.c\",\n              \"$src/json65-quote.s\", \"$src/json65-print.c\",\n              \"$test/test-print.c\");\n\n# test-file uses library functions (ftell and fseek) which are not available\n# on sim65, so we build it for Apple II instead.  This means that we cannot\n# test it automatically, though.  (But it's still worth building, to make\n# sure it builds.)\nbuild_program({'prog' => \"$test/testfile.system\",\n               'target' => 'apple2',\n               'config' => 'apple2-system.cfg'},\n              \"$src/json65.s\", \"$src/json65-file.c\", \"$test/test-file.c\");\nbuild_program({'prog' => \"$example/example.system\",\n               'target' => 'apple2',\n               'config' => 'apple2-system.cfg'},\n              \"$src/json65.s\", \"$src/json65-file.c\",\n              \"$src/json65-string.s\", \"$src/json65-tree.c\",\n              \"$src/json65-quote.s\", \"$src/json65-print.c\",\n              \"$example/example.c\");\n\nchdir ($test);\n\n# Run tests on sim65\nrun_test (\"test\");\nrun_test (\"test-string\");\nrun_test (\"test-tree\");\nrun_test (\"test-print\");\n\n# test-quote is not self-checking, and its functionality is subsumed\n# by test-print, so there's no need to run it\n#run_test (\"test-quote\");\n\nprint_heading \"Size summary\";\n\nmy $print_map = parse_map (\"test-print.map\");\nmy $file_map = parse_map (\"testfile.system.map\");\n\nprint_size ($print_map, \"json65.o\");\nprint_size ($print_map, \"json65-string.o\");\nprint_size ($print_map, \"json65-tree.o\");\nprint_size ($print_map, \"json65-quote.o\");\nprint_size ($print_map, \"json65-print.o\");\nprint_size ($file_map,  \"json65-file.o\");\nprintf \"%-15s %4u bytes\\n\", \"total\", $total_bytes;\n\nmy $failures = 0;\n\nprint_heading \"Test summary\";\nforeach my $t (sort keys %test_results) {\n    printf \"%-11s \", $t;\n    if ($test_results{$t} eq \"pass\") {\n        print $green, \"PASS\", $off, \"\\n\";\n    } else {\n        print $red, \"FAIL\", $off, \"\\n\";\n        $failures++;\n    }\n}\n\nexit $failures;\n"
  },
  {
    "path": "src/json65-file.c",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#include <errno.h>\n#include <string.h>\n#include \"json65-file.h\"\n\nstatic const char insufficient_memory[] = \"insufficient memory\";\n\nstatic const char *get_errmsg (int8_t n) {\n    switch (n) {\n    case J65_PARSE_ERROR:        return \"parse error\";\n    case J65_ILLEGAL_CHAR:       return \"illegal character\";\n    case J65_ILLEGAL_ESCAPE:     return \"illegal escape sequence\";\n    case J65_STRING_TOO_LONG:    return \"string longer than 255 bytes\";\n    case J65_EXPECTED_STRING:    return \"keys must be strings\";\n    case J65_EXPECTED_COLON:     return \"expected ':'\";\n    case J65_EXPECTED_COMMA:     return \"expected ','\";\n    case J65_EXPECTED_OBJ_END:   return \"expected '}'\";\n    case J65_EXPECTED_ARRAY_END: return \"expected ']'\";\n    default: return NULL;\n    }\n}\n\nint8_t __fastcall__ j65_parse_file (FILE *f,\n                                    void *scratch,\n                                    size_t scratch_len,\n                                    void *ctx,\n                                    j65_callback cb,\n                                    uint8_t max_depth,\n                                    FILE *err,\n                                    uint8_t width,\n                                    const char *filename,\n                                    j65_err_func user_err_func) {\n    j65_parser *p;\n    char *buf;\n    size_t buflen;\n    long origin;\n    int8_t ret;\n    size_t size;\n    uint32_t line_num, column_num;\n    const char *errmsg;\n    uint32_t offset;\n\n    if (scratch_len < sizeof (j65_parser) + width + 1) {\n        fprintf (err, \"%s %u\\n\", insufficient_memory, scratch_len);\n        return J65_INSUFFICIENT_MEMORY;\n    }\n\n    p = (j65_parser *) scratch;\n    buf = ((char *) scratch) + sizeof (j65_parser);\n    buflen = scratch_len - sizeof (j65_parser);\n\n    j65_init (p, ctx, cb, max_depth);\n    /* I am having a weird problem with ftell returning 512 when\n    ** I am at the beginning of the file.  Until I can figure that\n    ** out, just assume that f is at the beginning of the file.\n    **\n    ** The bug report is here:\n    ** https://github.com/cc65/cc65/issues/722\n    */\n#if 1\n    origin = 0;\n#else\n    origin = ftell (f);\n    if (origin < 0)\n        goto io_error;\n#endif\n\n    do {\n        size = fread (buf, 1, buflen, f);\n        if (ferror (f))\n            goto io_error;\n        ret = j65_parse (p, buf, size);\n    } while (ret == J65_WANT_MORE && !feof (f));\n\n    if (ret == J65_WANT_MORE) {\n        fprintf (err, \"%s: Unexpected end of file\\n\", filename);\n        return J65_UNEXPECTED_END_OF_FILE;\n    }\n\n    if (ret == J65_DONE) {\n        return ret;\n    }\n\n    line_num = j65_get_line_number (p);\n    column_num = j65_get_column_number (p);\n\n    fprintf (err, \"%s:%lu:%lu: \", filename, line_num + 1, column_num + 1);\n    errmsg = get_errmsg (ret);\n    if (errmsg != NULL) {\n        fputs (errmsg, err);\n    } else if (ret == J65_NESTING_TOO_DEEP) {\n        fprintf (err, \"exceeded max nesting depth of %u levels\",\n                 j65_get_max_depth (p));\n    } else {\n        if (user_err_func == NULL)\n            user_err_func = j65_default_err_func;\n        user_err_func (err, ctx, ret);\n    }\n    fputc ('\\n', err);\n\n    width--;\n    offset = j65_get_line_offset (p);\n    if (column_num >= width) {\n        offset += (column_num - (width - 1));\n        column_num = width - 1;\n    }\n\n    if (fseek (f, origin + offset, SEEK_SET) < 0)\n        goto io_error;\n\n    size = fread (buf, 1, width, f);\n    buf[size] = 0;\n    size = strcspn (buf, \"\\r\\n\");\n    buf[size] = 0;\n    fprintf (err, \"%s\\n\", buf);\n\n    memset (buf, ' ', column_num);\n    buf[column_num] = '^';\n    buf[column_num + 1] = 0;\n\n    fprintf (err, \"%s\\n\", buf);\n\n    return ret;\n\n io_error:\n    if (_oserror)\n        errmsg = _stroserror (_oserror);\n    else\n        errmsg = strerror (errno);\n    fprintf (err, \"%s: %s\\n\", filename, errmsg);\n    return J65_IO_ERROR;\n}\n\nvoid __fastcall__ j65_default_err_func (FILE *err,\n                                        void *ctx,\n                                        int8_t status) {\n    if (status == J65_INSUFFICIENT_MEMORY) {\n        fputs (insufficient_memory, err);\n    } else {\n        fprintf (err, \"Unknown error code %d\", status);\n    }\n}\n"
  },
  {
    "path": "src/json65-file.h",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#ifndef J65_FILE_H\n#define J65_FILE_H\n\n#include <stdint.h>\n#include <stdio.h>\n\n#include \"json65.h\"\n\n/*\n  These are additional error codes that can be returned, besides\n  the ones in j65_status.\n */\nenum {\n    J65_INSUFFICIENT_MEMORY    = -1, /* scratch_len was too small */\n    J65_IO_ERROR               = -2, /* file I/O returned an error */\n    J65_UNEXPECTED_END_OF_FILE = -3, /* incomplete JSON value */\n};\n\n/*\n  This optional callback function should translate your custom\n  error code (in the status argument) to a human-readable\n  string, and print it on the err filehandle.  The ctx argument\n  is supplied in case you want your error message to contain\n  additional information from the context.\n\n  If you do not recognize the error code, you can pass it along\n  to j65_default_err_func().\n */\ntypedef void __fastcall__ (*j65_err_func) (FILE *err,\n                                           void *ctx,\n                                           int8_t status);\n\n/*\n  Parses JSON from a file.  If an error occurs, prints a nice\n  human-readable error message, including the line number and\n  column number, and prints the offending line with a caret\n  pointing to the offending position.\n\n  Does not do any dynamic memory allocation.  However, it requires a\n  chunk of scratch memory.  The scratch memory must be at least 513 +\n  width bytes, and I would recommend at least 768 bytes, though 1K or\n  2K might be preferable, from a performance standpoint.  (But,\n  experiment and see what works best on your operating system.)\n  The scratch buffer does not need to be big enough to hold the whole\n  file, though.\n\n  Returns J65_DONE if the file is parsed successfully.  If an error\n  occurs, returns a negative number which will either be one of the\n  error codes from j65_status in json65.h, or one of the error codes\n  in the enumation at the top of this file.  In the case of an error,\n  a human-readable message will have been printed to the err\n  filehandle (so you may not need to care which negative value was\n  returned, just that it was negative).  Never returns J65_WANT_MORE.\n  (Feeding chunks of the file is handled automatically, and if\n  J65_WANT_MORE occurs at the end of the file, that is translated\n  into J65_UNEXPECTED_END_OF_FILE.)\n\n  Here are the arguments:\n\n  f - The file to parse.\n\n  scratch - Pointer to scratch memory.\n\n  scratch_len - Size of scratch memory, in bytes.  If it is\n  not at least 513 + width bytes, the function returns\n  J65_INSUFFICIENT_MEMORY.\n\n  ctx - Context argument which will be passed to callback.  (To pass\n  a pointer to any data structure you desire.)\n\n  cb - The callback which is called for each event in the JSON file.\n  For more information, see documentation in json65.h.\n\n  max_depth - The maximum nesting depth (of nested arrays and objects)\n  allowed.  If you do not care, set it to 0.  See documentation for\n  j65_init() in json.h for additional information.\n\n  err - The file handle (such as stdout, stderr, or an actual file)\n  to print the error message to, if an error occurs.\n\n  width - The width, in characters, of the display device for the\n  err filehandle.  If the err filehandle represents the screen, you\n  can get the width with the screensize() function from the cc65\n  standard header file conio.h.  If err is a real file, then just\n  choose something reasonable, like 80.\n\n  filename - The name of the file represented by the f filehandle.\n  This name is only used in generating an error message; we do not\n  attempt to access the file by name.\n\n  user_err_func - If you return custom error codes from your\n  callback function, you can supply a user_err_func which\n  translates your custom error codes to a human-readable string.\n  Passing NULL is the same as passing j65_default_err_func.\n */\nint8_t __fastcall__ j65_parse_file (FILE *f,\n                                    void *scratch,\n                                    size_t scratch_len,\n                                    void *ctx,\n                                    j65_callback cb,\n                                    uint8_t max_depth,\n                                    FILE *err,\n                                    uint8_t width,\n                                    const char *filename,\n                                    j65_err_func user_err_func);\n\n/*\n  A default implementation of j65_err_func, which prints\n  unknown error codes numerically.\n */\nvoid __fastcall__ j65_default_err_func (FILE *err,\n                                        void *ctx,\n                                        int8_t status);\n\n#endif  /* J65_FILE_H */\n"
  },
  {
    "path": "src/json65-print.c",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#include <stdbool.h>\n#include \"json65-print.h\"\n#include \"json65-quote.h\"\n\nenum {\n    ASCENDING,\n    DESCENDING,\n};\n\n/* This is a non-recursive implementation because the tree might be deep\n * and the 6502 stack is not very deep. */\nint __fastcall__ j65_print_tree (j65_node *root, FILE *f) {\n    j65_node *n = root;\n    uint8_t direction = DESCENDING;\n    bool done = false;\n    char c1, c2;\n    j65_node *next;\n    uint8_t next_direction;\n    bool print_comma;\n    uint8_t node_type;\n\n    if (root == NULL)\n        return 0;\n\n    do {\n        node_type = n->node_type;\n\n        /* the default (for scalars) is to visit the next sibling */\n        if (n->next) {\n            next = n->next;\n            next_direction = DESCENDING;\n            print_comma = true;\n        } else {\n            next = n->parent;\n            next_direction = ASCENDING;\n            print_comma = false;\n        }\n\n        switch (node_type) {\n        case J65_NULL:\n            fputs (\"null\", f);\n            break;\n        case J65_FALSE:\n            fputs (\"false\", f);\n            break;\n        case J65_TRUE:\n            fputs (\"true\", f);\n            break;\n        case J65_INTEGER:\n            fprintf (f, \"%ld\", n->integer);\n            break;\n        case J65_NUMBER:\n            fputs (n->string, f);\n            break;\n        case J65_STRING:\n            fputc ('\\\"', f);\n            j65_print_escaped (n->string, f);\n            fputc ('\\\"', f);\n            break;\n        case J65_KEY:\n            if (direction == DESCENDING) {\n                fputc ('\\\"', f);\n                j65_print_escaped (n->string, f);\n                fputs (\"\\\":\", f);\n                next = n->child;\n                next_direction = DESCENDING;\n                print_comma = false;\n            }\n            break;\n        case J65_START_OBJ:\n        case J65_START_ARRAY:\n            if (node_type == J65_START_OBJ) {\n                c1 = '{';\n                c2 = '}';\n            } else {\n                c1 = '[';\n                c2 = ']';\n            }\n            if (direction == DESCENDING) {\n                fputc (c1, f);\n                next = n->child;\n                if (next) {\n                    next_direction = DESCENDING;\n                } else {\n                    next = n;\n                    next_direction = ASCENDING;\n                }\n                print_comma = false;\n            } else {\n                fputc (c2, f);\n            }\n            break;\n        default:\n            /* should never happen... */\n            fprintf (f, \"?%u(%p)\", node_type, n);\n            break;\n        }\n\n        if (n == root && (direction == ASCENDING ||\n                          (node_type != J65_START_OBJ &&\n                           node_type != J65_START_ARRAY))) {\n            done = true;\n        } else if (print_comma) {\n            fputc (',', f);\n        }\n\n        n = next;\n        direction = next_direction;\n    } while (!done && !ferror(f));\n\n    if (ferror (f))\n        return -1;\n    else\n        return 0;\n}\n"
  },
  {
    "path": "src/json65-print.h",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#ifndef J65_PRINT_H\n#define J65_PRINT_H\n\n#include <stdio.h>\n#include \"json65-tree.h\"\n\n/*\n  Prints the specified tree to the specified file handle as JSON.\n\n  Returns 0 on success.  If an error occurs on the file handle,\n  returns -1.  In that case, look at errno and/or _oserror to\n  see what the error was.\n */\nint __fastcall__ j65_print_tree (j65_node *n, FILE *f);\n\n#endif  /* J65_PRINT_H */\n"
  },
  {
    "path": "src/json65-quote.h",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#ifndef J65_QUOTE_H\n#define J65_QUOTE_H\n\n#include <stdio.h>\n\n/*\n  Prints the specified string to the specified file handle.\n\n  Special characters are escaped using the escape sequences\n  from the JSON specification.\n\n  Does not return a value, so to check for an error, you\n  should call ferror(f).\n */\nvoid __fastcall__ j65_print_escaped (const char *str, FILE *f);\n\n#endif  /* J65_QUOTE_H */\n"
  },
  {
    "path": "src/json65-quote.s",
    "content": ";; JSON65 - A JSON parser for the 6502 microprocessor.\n;;\n;; https://github.com/ppelleti/json65\n;;\n;; Copyright © 2018 Patrick Pelletier\n;;\n;; This software is provided 'as-is', without any express or implied\n;; warranty.  In no event will the authors be held liable for any damages\n;; arising from the use of this software.\n;;\n;; Permission is granted to anyone to use this software for any purpose,\n;; including commercial applications, and to alter it and redistribute it\n;; freely, subject to the following restrictions:\n;;\n;; 1. The origin of this software must not be misrepresented; you must not\n;;    claim that you wrote the original software. If you use this software\n;;    in a product, an acknowledgment in the product documentation would be\n;;    appreciated but is not required.\n;; 2. Altered source versions must be plainly marked as such, and must not be\n;;    misrepresented as being the original software.\n;; 3. This notice may not be removed or altered from any source distribution.\n\n        .macpack generic\n        .include \"zeropage.inc\"\n\n        .import _fprintf\n        .import _fputc\n        .import _fwrite\n        .import popax\n        .import pushax\n\n        .export _j65_print_escaped\n\n        fileptr = regbank\n        strptr = regbank + 2\n        startidx = regbank + 4\n        saveidx = regbank + 5\n        t1 = tmp1\n        len = tmp2\n        character = tmp3\n\n;; pushes regbank (caller-saved registers) onto 6502 stack\n.macro save_regbank\n        .repeat 6, i\n        lda regbank+i\n        pha\n        .endrep\n.endmacro               ; save_regbank\n\n;; pops regbank (caller-saved registers) off of 6502 stack\n.macro restore_regbank\n        .repeat 6, i\n        pla\n        sta regbank+5-i\n        .endrep\n.endmacro               ; restore_regbank\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                         j65_print_escaped                        ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; void __fastcall__ j65_print_escaped (const char *str, FILE *f)\n.proc _j65_print_escaped\n        sta t1\n        save_regbank\n        lda t1\n        sta fileptr\n        stx fileptr+1\n        jsr popax\n        sta strptr\n        stx strptr+1\n        ldy #0\nloop1:  sty startidx\nloop2:  lda (strptr),y\n        cmp #'#'                ; check whether character is special\n        bge higher\n        and #$fe\n        cmp #' '\n        bne special_char\nokay_char:                      ; character does not need to be escaped\n        iny\n        jmp loop2\nhigher: cmp #$5c                ; backslash\n        bne okay_char\nspecial_char:                   ; character needs to be escaped\n        sty saveidx\n        tya\n        sub startidx\n        beq skip_fwrite         ; skip fwrite if \"count\" would be 0\n        sta len\n        ldx strptr+1            ; add strptr to startidx to make \"buf\" argument\n        lda strptr\n        add startidx\n        bcc skip_inx\n        inx\nskip_inx:\n        jsr pushax              ; push argument \"buf\"\n        lda #1\n        ldx #0\n        jsr pushax              ; push argument \"size\"\n        lda len\n        ldx #0\n        jsr pushax              ; push argument \"count\"\n        lda fileptr             ; argument \"f\" passed in ax\n        ldx fileptr+1\n        jsr _fwrite\nskip_fwrite:\n        ldy saveidx\n        lda (strptr),y\n        beq done                ; terminating NUL character\n        pha                     ; save character to be escaped\n        lda #$5c                ; print backslash\n        ldx #0\n        jsr pushax              ; push argument \"c\"\n        lda fileptr             ; argument \"f\" passed in ax\n        ldx fileptr+1\n        jsr _fputc\n        pla                     ; restore character to be escaped\n        sta character\n        ldx #6                  ; see if there is a short escape for character\nesc_loop:\n        lda escaped_chars,x\n        cmp character\n        beq found_short_escape\n        dex\n        bpl esc_loop\n        lda fileptr             ; there is not a short escape, use \\uxxxx\n        ldx fileptr+1\n        jsr pushax              ; push argument \"f\"\n        lda #<fmt_str\n        ldx #>fmt_str\n        jsr pushax              ; push argument \"format\"\n        lda character\n        ldx #0\n        jsr pushax              ; push varargs argument (character as int)\n        ldy #6                  ; bytes of arguments on the stack\n        jsr _fprintf\ncontinue:\n        ldy saveidx\n        iny\n        jmp loop1\nfound_short_escape:\n        lda escape_codes,x      ; print escape code character\n        ldx #0\n        jsr pushax              ; push argument \"c\"\n        lda fileptr             ; argument \"f\" passed in ax\n        ldx fileptr+1\n        jsr _fputc\n        jmp continue\ndone:   restore_regbank\n        rts\n\n        .rodata\nescape_codes:\n        .byte $22, $5c, \"bfnrt\"\nescaped_chars:\n        .byte $22, $5c, $08, $0c, $0a, $0d, $09\nfmt_str:\n        .asciiz \"u%04x\"\n\n.endproc                ; _j65_print_escaped\n"
  },
  {
    "path": "src/json65-string.h",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#ifndef J65_STRING_H\n#define J65_STRING_H\n\n#include <stdint.h>\n\n/*\n  j65_strings is a \"string intern pool\" used for \"interning\"\n  string values:\n\n  https://en.wikipedia.org/wiki/String_interning\n\n  This saves space by only keeping a single copy of each string,\n  and it also means interned strings can be compared by pointer\n  value, without having to compare character-by-character.\n\n  j65_strings is too large to fit on the stack, so it should be\n  allocated statically or on the heap.\n\n  To initialize the intern pool, call j65_init_strings().\n  Since j65_intern_string() will allocate memory with malloc()\n  for each unique interned string, you must call j65_free_strings()\n  to free that memory when you are done with the intern pool.\n\n  Internally, j65_strings is implemented as a fixed-size,\n  256-entry hash table, where each hash bucket is a linked list\n  to handle collisions.\n\n  j65_strings has an implementation limit: It only supports\n  strings of 255 bytes or less.  If a string is longer than\n  255 bytes, the interned string is truncated at 255 bytes.\n  This should not be an issue when interning strings returned\n  by the JSON65 parser, since the JSON65 parser also has a\n  limit of 255 bytes for strings.\n */\ntypedef struct {\n    uint8_t opaque[512];\n} j65_strings;\n\n/*\n  Initialize a string intern pool for use.  Once you have initialized\n  it, you may call j65_intern_string() on it.\n */\nvoid __fastcall__ j65_init_strings (j65_strings *strs);\n\n/*\n  Intern a string in the specified pool.  The returned pointer\n  will strcmp() equal to the str argument, as long as the str\n  argument is 255 bytes or less in length.  (If str is longer,\n  then the interned string will be truncated to 255 bytes.)\n\n  The returned pointer will be valid until j65_free_strings()\n  is called on the pool.\n\n  If the specified string does not already exist in the pool,\n  and malloc() returns NULL when allocating memory for the new\n  string, then j65_intern_string() will return NULL.\n*/\nconst char * __fastcall__ j65_intern_string (j65_strings *strs,\n                                             const char *str);\n\n/*\n  Frees all memory used by the given string pool.  Once\n  j65_free_strings() is called, all of the pointers\n  returned by j65_intern_string() for this pool become\n  invalid.\n */\nvoid __fastcall__ j65_free_strings (j65_strings *strs);\n\n#endif  /* J65_STRING_H */\n"
  },
  {
    "path": "src/json65-string.s",
    "content": ";; JSON65 - A JSON parser for the 6502 microprocessor.\n;;\n;; https://github.com/ppelleti/json65\n;;\n;; Copyright © 2018 Patrick Pelletier\n;;\n;; This software is provided 'as-is', without any express or implied\n;; warranty.  In no event will the authors be held liable for any damages\n;; arising from the use of this software.\n;;\n;; Permission is granted to anyone to use this software for any purpose,\n;; including commercial applications, and to alter it and redistribute it\n;; freely, subject to the following restrictions:\n;;\n;; 1. The origin of this software must not be misrepresented; you must not\n;;    claim that you wrote the original software. If you use this software\n;;    in a product, an acknowledgment in the product documentation would be\n;;    appreciated but is not required.\n;; 2. Altered source versions must be plainly marked as such, and must not be\n;;    misrepresented as being the original software.\n;; 3. This notice may not be removed or altered from any source distribution.\n\n        .macpack generic\n        .include \"zeropage.inc\"\n\n        .import _free\n        .import _malloc\n        .import popax\n\n        .export _j65_init_strings\n        .export _j65_intern_string\n        .export _j65_free_strings\n\n        ;; take advantage of the fact that malloc and free don't\n        ;; modify regsave or sreg\n        loptr = regsave\n        hiptr = regsave + 2\n        linkptr = sreg\n        ;; they don't modify tmp1 through tmp4, either\n        t1 = tmp1\n        t2 = tmp2\n        hash_val = tmp3\n        len = tmp4\n        idx = tmp4\n        ;; ptr4 is also preserved across malloc (but not free)\n        strptr = ptr4\n        ;; the following doesn't need to be preserved across\n        ;; a malloc or free\n        tmpptr = ptr3\n\n        ;; bucket format:\n        ;; lo byte of ptr to next bucket\n        ;; hi byte of ptr to next bucket\n        ;; length of string\n        ;; 0-255 bytes of string\n        ;; NUL byte\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                         j65_init_strings                         ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; void __fastcall__ j65_init_strings (j65_strings *strs);\n.proc _j65_init_strings\n        sta ptr1\n        stx ptr1+1\n        ldy #0\n        tya\n        jsr loop\n        inc ptr1+1\nloop:   sta (ptr1),y\n        iny\n        bne loop\n        rts\n.endproc                ; _j65_init_strings\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                         j65_intern_string                        ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; const char *j65_intern_string (j65_strings *strs, const char *str);\n.proc _j65_intern_string\n        sta strptr\n        stx strptr+1\n        jsr hash_str\n        sta hash_val\n        sty len\n        jsr popax               ; get pointer to j65_strings structure\n        sta loptr\n        stx loptr+1\n        sta hiptr\n        inx\n        stx hiptr+1\n        ldy hash_val            ; lookup hash bucket\n        lda (loptr),y\n        sta linkptr\n        lda (hiptr),y\nlinkloop:                       ; traverse linked list\n        sta linkptr+1\n        ora linkptr\n        beq not_found\n        ldy #2\n        lda (linkptr),y\n        cmp len\n        bne nextlink\n        lda linkptr             ; compare string\n        add #3\n        sta tmpptr\n        lda linkptr+1\n        adc #0\n        sta tmpptr+1\n        ldy #0\nstrloop:\n        lda (strptr),y\n        cmp (tmpptr),y\n        bne nextlink\n        iny\n        cpy len\n        bne strloop\n        lda tmpptr              ; return pointer to string from table\n        ldx tmpptr+1\nfail:   rts\nnextlink:\n        ldy #0\n        lda (linkptr),y\n        tax\n        iny\n        lda (linkptr),y\n        stx linkptr\n        jmp linkloop\nnot_found:                      ; so we need to add it to the hash table\n        ldx #0\n        lda len\n        add #4\n        bcc skip\n        inx\nskip:   jsr _malloc\n        sta tmpptr\n        stx tmpptr+1\n        ora tmpptr+1\n        beq fail                ; if malloc returned null, we return null\n        ldy hash_val\n        lda (hiptr),y           ; add new memory to linked list\n        tax\n        lda (loptr),y\n        ldy #0\n        sta (tmpptr),y\n        iny\n        txa\n        sta (tmpptr),y\n        ldy hash_val\n        lda tmpptr\n        sta (loptr),y\n        lda tmpptr+1\n        sta (hiptr),y\n        ldy #2                  ; store length\n        lda len\n        sta (tmpptr),y\n        lda tmpptr              ; copy string\n        add #3\n        sta tmpptr\n        bcc skip2\n        inc tmpptr+1\nskip2:  ldy #0\ncopyloop:\n        cpy len\n        beq copydone\n        lda (strptr),y\n        sta (tmpptr),y\n        iny\n        jmp copyloop\ncopydone:\n        lda #0\n        sta (tmpptr),y\n        lda tmpptr              ; return pointer to new string\n        ldx tmpptr+1\n        rts\n.endproc                ; _j65_intern_string\n\n;; rotate accumulator left by 1.\n.macro rotate_left\n        cmp #$80\n        rol\n.endmacro               ; rotate_left\n\n;; hash the (NUL terminated) string pointed at by strptr.\n;; return hash in a and length in y.\n.proc hash_str\n        lda #0\n        tay\nloop:   sta t1\n        lda (strptr),y\n        beq done\n        iny\n        beq done0\n        add t1\n        rotate_left\n        rotate_left\n        rotate_left\n        jmp loop\ndone0:  dey\ndone:   lda t1\n        rts\n.endproc                ; hash_str\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                         j65_free_strings                         ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; void __fastcall__ j65_free_strings (j65_strings *strs);\n.proc _j65_free_strings\n        sta loptr\n        stx loptr+1\n        sta hiptr\n        inx\n        stx hiptr+1\n        ldy #0\nloop:   sty idx\n        lda (hiptr),y\n        tax\n        lda (loptr),y\n        jsr freelink\n        ldy idx\n        lda #0\n        sta (loptr),y\n        sta (hiptr),y\n        iny\n        bne loop\n        rts\n.endproc                ; _j65_free_strings\n\n;; free the chain of buckets pointed to by ax\n.proc freelink\n        sta linkptr\n        stx linkptr+1\n        ora linkptr+1\n        beq done\n        ldy #0\n        lda (linkptr),y\n        sta t1\n        iny\n        lda (linkptr),y\n        sta t2\n        lda linkptr\n        ldx linkptr+1\n        jsr _free\n        lda t1\n        ldx t2\n        jmp freelink\ndone:   rts\n.endproc                ; freelink\n"
  },
  {
    "path": "src/json65-tree.c",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#include <stdbool.h>\n#include <stdlib.h>             /* malloc and free */\n#include \"json65-tree.h\"\n\ntypedef struct {\n    j65_strings strings;\n    j65_node *root;\n    j65_node *current;\n    bool add_child;\n} j65_tree_internal;\n\nvoid __fastcall__ j65_init_tree (j65_tree *t) {\n    j65_tree_internal *tree = (j65_tree_internal *) t;\n    j65_init_strings (&tree->strings);\n    tree->root = NULL;\n    tree->current = NULL;\n    tree->add_child = true;\n}\n\nint8_t __fastcall__ j65_tree_callback (j65_parser *p, uint8_t event) {\n    j65_tree_internal *tree = (j65_tree_internal *) j65_get_context (p);\n    const char *str = NULL;\n    j65_node *n;\n\n    switch (event) {\n    case J65_END_OBJ:\n    case J65_END_ARRAY:\n        if (tree->add_child) {\n            tree->add_child = false;\n        } else {\n            tree->current = tree->current->parent;\n        }\n        if (tree->current->parent &&\n            tree->current->parent->node_type == J65_KEY) {\n            tree->current = tree->current->parent;\n        }\n        return 0;\n    case J65_NUMBER:\n    case J65_STRING:\n    case J65_KEY:\n        str = j65_intern_string (&tree->strings, j65_get_string (p));\n        if (str == NULL)\n            return J65_OUT_OF_MEMORY;\n    }\n\n    n = (j65_node *) malloc (sizeof (j65_node));\n    if (n == NULL)\n        return J65_OUT_OF_MEMORY;\n\n    n->node_type = event;\n    n->location.line_offset = j65_get_line_offset (p);\n    n->location.line_number = j65_get_line_number (p);\n    n->location.column_number = j65_get_column_number (p);\n\n    if (tree->add_child)\n        n->parent = tree->current;\n    else\n        n->parent = tree->current->parent;\n    n->next = NULL;\n\n    n->integer = 0;           /* clears all fields of union to NULL */\n\n    switch (event) {\n    case J65_INTEGER:\n        n->integer = j65_get_integer (p);\n        break;\n    case J65_KEY:\n    case J65_NUMBER:\n    case J65_STRING:\n        n->string = str;\n        break;\n    }\n\n    if (tree->current == NULL) {\n        tree->root = n;\n        tree->current = n;\n    } else if (tree->add_child) {\n        tree->current->child = n;\n        if (tree->current->node_type == J65_KEY) {\n            if (event == J65_START_OBJ || event == J65_START_ARRAY)\n                tree->current = n;\n        } else {\n            tree->current = n;\n        }\n    } else {\n        tree->current->next = n;\n        tree->current = n;\n    }\n\n    switch (event) {\n    case J65_KEY:\n    case J65_START_OBJ:\n    case J65_START_ARRAY:\n        tree->add_child = true;\n        break;\n    default:\n        tree->add_child = false;\n        break;\n    }\n\n    return 0;\n}\n\nj65_node * __fastcall__ j65_find_key (j65_tree *t,\n                                      j65_node *object,\n                                      const char *key) {\n    const char *k = j65_intern_string (&t->strings, key);\n\n    if (k == NULL)\n        return NULL;\n\n    return j65_find_interned_key (object, k);\n}\n\nj65_node * __fastcall__ j65_find_interned_key (j65_node *object,\n                                               const char *key) {\n    j65_node *n;\n    if (object->node_type == J65_START_OBJ)\n        n = object->child;\n    else if (object->node_type == J65_KEY)\n        n = object;\n    else\n        return NULL;\n\n    while (n != NULL) {\n        if (n->string == key)\n            return n;\n        n = n->next;\n    }\n\n    return NULL;\n}\n\nvoid __fastcall__ j65_free_tree (j65_tree *t) {\n    j65_tree_internal *tree = (j65_tree_internal *) t;\n    j65_node *n = tree->root;\n    j65_node *follow;\n\n    /* traverse the entire tree without recursion */\n    /* (since 6502 stack is limited) */\n    while (n != NULL) {\n        switch (n->node_type) {\n        case J65_KEY:\n        case J65_START_OBJ:\n        case J65_START_ARRAY:\n            follow = n->child;\n            n->child = NULL;\n            if (follow != NULL)\n                break;\n            /* fall thru */\n        default:\n            /* node has no children (either a leaf or empty container) */\n            follow = n->next;\n            if (follow == NULL) /* no remaining siblings, either */\n                follow = n->parent;\n            free (n);\n        }\n        n = follow;\n    }\n\n    tree->root = NULL;\n    tree->current = NULL;\n    tree->add_child = true;\n\n    j65_free_strings (&tree->strings);\n}\n"
  },
  {
    "path": "src/json65-tree.h",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#ifndef J65_TREE_H\n#define J65_TREE_H\n\n#include \"json65.h\"\n#include \"json65-string.h\"\n\n/* in addition to the status codes from j65_status */\nenum {\n    J65_OUT_OF_MEMORY = -1,     /* malloc returned NULL */\n};\n\n/*\n  Specifies the location in the input JSON file that corresponds\n  to a particular node.\n\n  line_number and column_number are 0-based, so you should probably\n  add 1 before displaying them to the user.\n\n  line_offset is the byte position in the file where the line\n  specified by line_number begins.  This is useful if you want\n  to display the line in question, because you can seek directly\n  to the beginning of the line in the file.\n\n  For arrays and objects, the position specified is that of the\n  open square bracket or open curly brace.  For scalars, the\n  position specified is at the end of the scalar.\n */\ntypedef struct {\n    uint32_t line_offset;\n    uint32_t line_number;\n    uint32_t column_number;\n} j65_source_location;\n\ntypedef struct j65_node j65_node;\n\n/*\n  j65_node represents a value parsed from the JSON file.\n\n  The node_type is recycled from the j65_event enumeration\n  in json65.h.  All event types are legal node types, with\n  the exception of J65_END_OBJ and J65_END_ARRAY.  Since\n  the tree structure treats objects and arrays as containers\n  rather than events, they do not have a separate start and\n  end.  Rather, J65_START_OBJ and J65_START_ARRAY are used\n  to specify object nodes and array nodes, respectively.\n\n  There are three types of container nodes: J65_START_ARRAY,\n  J65_START_OBJ, and J65_KEY.  Array nodes may contain any\n  number of children, of any type except J65_KEY.  Object\n  nodes may contain any number of children, but they may\n  only be of type J65_KEY.  Key nodes must have exactly one\n  child, which may be of any type except J65_KEY.\n\n  Every node except the root node has a parent, which is the\n  J65_START_ARRAY, J65_START_OBJ, or J65_KEY node which\n  contains it.  For the root node, the parent pointer is NULL.\n\n  Container nodes point to their first child.  Each child\n  node points to its next sibling via the next pointer.\n  The final sibling has a NULL next pointer.\n */\nstruct j65_node {\n    uint8_t node_type;\n    j65_source_location location;\n    j65_node *parent;\n    j65_node *next;\n    union {\n        int32_t integer;        /* J65_INTEGER */\n        struct {\n            const char *string; /* J65_KEY, J65_NUMBER, or J65_STRING */\n            j65_node *child;    /* J65_KEY, J65_START_OBJ, or J65_START_ARRAY */\n        };\n    };\n};\n\n/*\n  This structure represents an entire tree of nodes read from\n  a JSON file.  The j65_tree should be initialized with\n  j65_init_tree(), and then it can be passed as the context\n  argument to j65_parse(), with j65_tree_callback() as the\n  callback argument.\n\n  Once parsing has completed, the tree will be populated.\n  The root pointer points to the root (top-level) node.\n  All of the strings contained in the tree (in J65_STRING,\n  J65_NUMBER, and J65_KEY nodes) will be interned in the\n  strings member.\n\n  Besides the string pool and the root pointer, j65_tree also\n  contains a small amount of bookkeeping information used\n  by the callback, which is unused once parsing is complete.\n */\ntypedef struct {\n    j65_strings strings;\n    j65_node *root;\n    uint8_t internal[3];\n} j65_tree;\n\n/*\n  Initializes the j65_tree structure for use.\n */\nvoid __fastcall__ j65_init_tree (j65_tree *t);\n\n/*\n  This should be specified as the callback to j65_parse(),\n  and the j65_tree structure should be specified as the\n  context.  This callback builds up the tree as the parser\n  generates events.\n */\nint8_t __fastcall__ j65_tree_callback (j65_parser *p, uint8_t event);\n\n/*\n  Interns the given string into the tree's intern pool, and\n  then searches for a J65_KEY child node of the given\n  J65_START_OBJ node whose key matches the given string.\n  The matching J65_KEY node is returned, or NULL if there is\n  no match.\n */\nj65_node * __fastcall__ j65_find_key (j65_tree *t,\n                                      j65_node *object,\n                                      const char *key);\n\n/*\n  Searches for a key node which matches the given string.  The\n  string is expected to have already been interned in the\n  tree's string intern pool.  The node passed in should be\n  of type J65_START_OBJ, and its J65_KEY children are\n  searched.  The matching J65_KEY node is returned, or NULL if\n  there is no match.\n */\nj65_node * __fastcall__ j65_find_interned_key (j65_node *object,\n                                               const char *key);\n\n/*\n  Frees all the memory used by this tree.  The tree is traversed,\n  and all of the nodes are freed.  Additionally, j65_free_strings()\n  is called on the string intern pool contained within the\n  j65_tree structure.\n */\nvoid __fastcall__ j65_free_tree (j65_tree *t);\n\n#endif  /* J65_TREE_H */\n"
  },
  {
    "path": "src/json65.h",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#ifndef J65_H\n#define J65_H\n\n#include <stdint.h>\n#include <stddef.h>             /* for size_t */\n\n/*\n  An event which is passed to the callback function, indicating\n  what has happened in the parser.  Most events have no associated\n  data, but a few have a string and/or an integer, as noted below,\n  which may be retrieved with j65_get_string() or j65_get_integer().\n */\nenum j65_event {\n    J65_NULL        = 0,\n    J65_FALSE       = 1,\n    J65_TRUE        = 2,\n    J65_INTEGER     = 3,        /* integer and string */\n    J65_NUMBER      = 4,        /* string */\n    J65_STRING      = 5,        /* string */\n    J65_KEY         = 6,        /* string */\n    J65_START_OBJ   = 7,\n    J65_END_OBJ     = 8,\n    J65_START_ARRAY = 9,\n    J65_END_ARRAY   = 10,\n};\n\n/*\n  A status value returned by j65_parse().  J65_DONE indicates that a\n  complete JSON value has been parsed successfully.  J65_WANT_MORE\n  indicates that j65_parse() should be called again with more input.\n  (If the end of the file has been reached, this can be considered an\n  \"unexpected end of file\" error.)\n\n  Negative values indicate errors.  The error may be one of the\n  predefined errors below, or it may be an error returned by the\n  callback.  User-defined error numbers should be between\n  J65_USER_ERROR and -1, inclusive.\n\n  If you want to define your own error codes, I recommend doing it\n  something like this:\n\n  enum {\n    MYERR_MISSING_REQUIRED_KEY = J65_USER_ERROR,\n    MYERR_UNSUPPORTED_KEY,\n    MYERR_VALUE_OUT_OF_RANGE,\n    MYERR_SOME_OTHER_ERROR,\n  };\n\n  This way, your error codes will begin at J65_USER_ERROR, and\n  grow upwards (towards 0).\n */\nenum j65_status {\n    J65_DONE      = 1,\n    J65_WANT_MORE = 2,\n\n    /* errors */\n    J65_PARSE_ERROR        = -128,\n    J65_ILLEGAL_CHAR,\n    J65_ILLEGAL_ESCAPE,\n    J65_NESTING_TOO_DEEP,\n    J65_STRING_TOO_LONG,\n    J65_EXPECTED_STRING,\n    J65_EXPECTED_COLON,\n    J65_EXPECTED_COMMA,\n    J65_EXPECTED_OBJ_END,\n    J65_EXPECTED_ARRAY_END,\n    J65_USER_ERROR,             /* must be last.  not generated by parser. */\n};\n\n/*\n  The state for a JSON parser.  Initialize it by calling j65_init().\n  It is too big to be allocated on the cc65 stack, so you should either\n  allocate it statically, or on the heap.\n */\ntypedef struct {\n    uint8_t dummy[512];\n} j65_parser;\n\n/*\n  The type of the callback function passed to j65_init.  This is called\n  from within j65_parse() whenever a parsing \"event\" occurs.  The event\n  type (the j65_event enumeration, but passed as a uint8_t) indicates\n  which event occurred.  Depending on the event type, additional\n  information may be available by calling one of the accessor functions\n  on the j65_parser.  (See functions below.)\n\n  If the event was processed successfully, the callback should return\n  zero.  If the callback wishes to terminate the parsing early, and\n  cause j65_parse() to return immediately, the callback should return\n  a negative value.  (Specifically, it should return an integer between\n  J65_USER_ERROR and -1, inclusive.)\n */\ntypedef int8_t __fastcall__ (*j65_callback)(j65_parser *p, uint8_t event);\n\n/*\n  Initializes a j65_parser structure.  The arguments passed will be\n  stored in the j65_parser structure.  Specifically:\n\n  ctx is a pointer with no predefined meaning.  It may be retrieved\n  by calling j65_get_context() on the parser.\n\n  cb is the callback which should be called by j65_parse() when an\n  event occurs.\n\n  max_depth is the maximum depth of nested objects and arrays allowed\n  when parsing the JSON.  max_depth will automatically be capped at\n  224.  It may sometimes be helpful to limit max_depth to a\n  smaller value.  (For example, if you are going to build up a tree\n  and then walk it recursively, the 6502 stack cannot hold 224\n  return addresses, so you could limit max_depth to a value somewhat\n  less than 128, to prevent overflowing the stack.)  To obtain the\n  value actually used for max_depth, call j65_get_max_depth() on the\n  parser.\n\n  If max_depth is 0, then it will be set to the maximum allowable\n  value, which is 224.  So, if you do not wish to further limit the\n  maximum depth, pass 0 for max_depth.  This means that the smallest\n  value you can actually set max_depth to is 1, which means that you\n  can only have a top-level array or object, but no arrays or objects\n  inside of it.  (Hypothetically, a max_depth of 0 would mean only\n  top-level scalars are accepted, and no arrays or objects would be\n  allowed at all.  But we do not let you set max_depth to 0.)\n\n  Once the j65_parser has been initialized, you may call j65_parse()\n  on it.\n */\nvoid __fastcall__ j65_init (j65_parser *p,\n                            void *ctx,\n                            j65_callback cb,\n                            uint8_t max_depth);\n\n/*\n  Parses the JSON in buf, of length len.  j65_parse() may be called\n  multiple times (as long as it returns J65_WANT_MORE) to parse input\n  incrementally.\n\n  j65_parse() calls the callback (supplied to j65_init()) as events\n  occur.\n\n  The return value is the j65_status enumeration, returned as an\n  int8_t.  J65_DONE indicates the parsing completed successfully.\n  J65_WANT_MORE indicates that j65_parse() should be called again\n  with more input.  Any negative value indicates an error, either\n  one generated by the parser, or one returned by the callback.\n */\nint8_t __fastcall__ j65_parse (j65_parser *p, const char *buf, size_t len);\n\n/*\n  Returns the string associated with the current event.  This call is\n  only valid inside the callback function, and only when the event is\n  one of J65_INTEGER, J65_NUMBER, J65_STRING, or J65_KEY.\n  The string returned is only valid until the callback returns.\n\n  The string is NUL-terminated, so it is not necessary to call\n  j65_get_length(), unless you wish to support strings with\n  embedded NUL characters.\n\n  In the case of a J65_NUMBER OR J65_KEY event, backslash escape\n  sequences in the string have already been substituted.  The string\n  is UTF-8 encoded.\n\n  In the case of a J65_NUMBER event, beware that although the number\n  has been validated to contain only characters that are legal in a\n  number, the number has not been fully validated to conform to the\n  format for a legal number (specified in section 6 of RFC 8259).\n  For example, the string might be \"10.10.10-e++\", which is not a\n  valid number.  So, you will need to more fully validate the number\n  when parsing it, and return a user error from the callback if\n  necessary.\n */\nconst char * __fastcall__ j65_get_string (const j65_parser *p);\n\n/*\n  In the cases where j65_get_string() is valid, j65_get_length() is\n  valid, too.  It returns the length of the string returned by\n  j65_get_string().  This is necessary if you wish to support\n  strings with embedded NUL characters, and it may also be useful\n  in other situations, such as if you want to avoid the performance\n  hit of calling strlen() on the string returned by j65_get_string().\n */\nuint8_t __fastcall__ j65_get_length (const j65_parser *p);\n\n/*\n  Returns the integer associated with the current event.  This call is\n  only valid inside the callback function, and only when the event is\n  J65_INTEGER.  Note that in a J65_INTEGER event, both j65_get_integer()\n  and j65_get_string() are valid, so you may process the integer\n  either as an integer or a string.\n\n  If a number cannot be represented as an int32_t, either because it\n  is non-integral, or because it is too large, then a J65_NUMBER\n  event is generated instead of J65_INTEGER.\n */\nint32_t __fastcall__ j65_get_integer (const j65_parser *p);\n\n/*\n  Returns the offset within the file of the beginning of the current\n  line.  (In other words, the total number of bytes processed by this\n  j65_parser, prior to the first byte of the current line.)\n\n  j65_get_line_offset() may be called either within the callback\n  function (for any event type), or after j65_parse() returns.\n\n  This can be useful if you want to print the current line when an\n  error occurs.\n*/\nuint32_t __fastcall__ j65_get_line_offset (const j65_parser *p);\n\n/*\n  Returns the line number of the current line.\n\n  j65_get_line_number() may be called either within the callback\n  function (for any event type), or after j65_parse() returns.\n\n  The line number is 0-based, so you will probably want to add 1\n  before displaying it to the user.\n\n  Lines may be terminated either by a linefeed (UNIX standard) or\n  a carriage return (Apple II standard).  However, a linefeed\n  is not counted if it is immediately preceded by a carriage return.\n  (Thus allowing MS-DOS/Windows standard line endings to be supported\n  as well.)\n*/\nuint32_t __fastcall__ j65_get_line_number (const j65_parser *p);\n\n/*\n  Like j65_get_line_number(), but returns the current column number\n  within the current line.  Like j65_get_line_number(), the column\n  number is 0-based, and may be obtained at any time, either during\n  or after parsing.\n\n  When inside the callback, the column number usually indicates the\n  last byte of the value which generated the current event, or the\n  byte immediately after it.  When j65_parse() returns with an error,\n  the column may either point to the byte where the error occurred,\n  or the last byte of the value which generated the error, or the\n  byte immediately following it.\n\n  The column number simply counts bytes, so the number may be\n  unintuitive if your input string contains tabs or multibyte\n  UTF-8 characters.\n */\nuint32_t __fastcall__ j65_get_column_number (const j65_parser *p);\n\n/*\n  Returns the current depth of nested arrays and objects.\n  It can be between 0 and max_depth (which is never more than\n  224), inclusive.  Depth 0 only occurs for top-level scalars.\n */\nuint8_t __fastcall__ j65_get_current_depth (const j65_parser *p);\n\n/*\n  Returns the maximum value that j65_get_current_depth() can have.\n  This is the value of max_depth which was supplied to j65_init(),\n  unless the value supplied was 0 or was greater than 224, in which\n  case the max depth will be 224.\n\n  If this depth is exceeded, the parser will return the error code\n  J65_NESTING_TOO_DEEP.\n */\nuint8_t __fastcall__ j65_get_max_depth (const j65_parser *p);\n\n/*\n  Returns the ctx pointer which was supplied to j65_init().\n  This can be used for passing arbitrary data to the callback.\n */\nvoid * __fastcall__ j65_get_context (const j65_parser *p);\n\n#endif  /* J65_H */\n"
  },
  {
    "path": "src/json65.s",
    "content": ";; JSON65 - A JSON parser for the 6502 microprocessor.\n;;\n;; https://github.com/ppelleti/json65\n;;\n;; Copyright © 2018 Patrick Pelletier\n;;\n;; This software is provided 'as-is', without any express or implied\n;; warranty.  In no event will the authors be held liable for any damages\n;; arising from the use of this software.\n;;\n;; Permission is granted to anyone to use this software for any purpose,\n;; including commercial applications, and to alter it and redistribute it\n;; freely, subject to the following restrictions:\n;;\n;; 1. The origin of this software must not be misrepresented; you must not\n;;    claim that you wrote the original software. If you use this software\n;;    in a product, an acknowledgment in the product documentation would be\n;;    appreciated but is not required.\n;; 2. Altered source versions must be plainly marked as such, and must not be\n;;    misrepresented as being the original software.\n;; 3. This notice may not be removed or altered from any source distribution.\n\n        .macpack generic\n        .include \"zeropage.inc\"\n\n;; routines from the cc65 runtime library\n        .import callptr4\n        .import incsp4\n        .import incsp6\n        .import negeax\n        .import pushax\n        .import resteax\n        .import saveeax\n\n        .export _j65_init\n        .export _j65_parse\n        .export _j65_get_string\n        .export _j65_get_length\n        .export _j65_get_integer\n        .export _j65_get_line_offset\n        .export _j65_get_line_number\n        .export _j65_get_column_number\n        .export _j65_get_current_depth\n        .export _j65_get_max_depth\n        .export _j65_get_context\n\n;; zero page locations\n        state     = regbank\n        strbuf    = regbank + 2\n        inbuf     = regbank + 4\n        inbuflast = tmp4         ; length - 1\n        charidx   = tmp3         ; position in inbuf\n        evtype    = tmp2         ; only used as an argument to call_callback\n        esc_code  = tmp2         ; only used as an argument to lookup_escape\n        tmp5      = sreg\n        tmp6      = sreg+1\n        long1     = regsave\n        long2     = ptr1\n;; zero page locations (for _j65_parse)\n        jlen      = ptr3\n\n;; character properties\n        prop_ws   = %10000000   ; must be hi bit (we use bmi/bpl to test)\n        prop_str  = %01000000\n        prop_lit  = %00100000\n        prop_int  = %00010000\n        prop_num  = %00001000\n        prop_sc   = %00000111   ; mask for structural character field\n\n;; j65_event\n.enum\n        J65_NULL        = 0\n        J65_FALSE       = 1\n        J65_TRUE        = 2\n        J65_INTEGER     = 3        ; integer and string\n        J65_NUMBER      = 4        ; string\n        J65_STRING      = 5        ; string\n        J65_KEY         = 6        ; string\n        J65_START_OBJ   = 7\n        J65_END_OBJ     = 8\n        J65_START_ARRAY = 9\n        J65_END_ARRAY   = 10\n.endenum\n\n;; j65_status\n.enum\n        J65_DONE      = 1\n        J65_WANT_MORE = 2\n\n        ; errors\n        J65_PARSE_ERROR        = $80\n        J65_ILLEGAL_CHAR\n        J65_ILLEGAL_ESCAPE\n        J65_NESTING_TOO_DEEP\n        J65_STRING_TOO_LONG\n        J65_EXPECTED_STRING\n        J65_EXPECTED_COLON\n        J65_EXPECTED_COMMA\n        J65_EXPECTED_OBJ_END\n        J65_EXPECTED_ARRAY_END\n        J65_USER_ERROR             ; must be last.  not generated by parser.\n.endenum\n\n;; lexer state\n.enum\n        lex_ready\n        lex_literal\n        lex_string\n        lex_str_escape\n.endenum\n\n;; parser state (should fit in 3 bits; otherwise need to change l_ready)\n;; (order needs to match dispatch_tab and literal_errors)\n.enum\n        par_ready\n        par_ready_or_close_array\n        par_key\n        par_key_or_close_object\n        par_need_colon\n        par_need_comma_or_close_array\n        par_need_comma_or_close_object\n        par_done\n.endenum\n\n;; structural characters (should fit in 3 bits)\n.enum\n        sc_none                 ; an illegal character\n        sc_lsq                  ; [\n        sc_lcur                 ; {\n        sc_rsq                  ; ]\n        sc_rcur                 ; }\n        sc_colon                ; :\n        sc_comma                ; ,\n        sc_quote                ; \"\n.endenum\n\n;; state variables\n.struct st\n        callback   .word        ; these two must be first and in this order\n        context    .word\n        file_off   .dword\n        line_off   .dword\n        line_num   .dword\n        col_num    .dword\n        long_val   .dword\n        lexer_st   .byte\n        parser_st  .byte\n        parser_st2 .byte\n        str_idx    .byte\n        stack_idx  .byte\n        flags      .byte\n        stack_min  .byte\n        prev_char  .byte\n.endstruct\n\n;; loads a with specified state variable.  clobbers y.\n.macro getstate arg\n        ldy #arg\n        lda (state),y\n.endmacro               ; getstate\n\n;; stores a to specified state variable.  clobbers y.\n.macro putstate arg\n        ldy #arg\n        sta (state),y\n.endmacro               ; putstate\n\n;; pushes regbank (caller-saved registers) onto 6502 stack\n.macro save_regbank\n        .repeat 6, i\n        lda regbank+i\n        pha\n        .endrep\n.endmacro               ; save_regbank\n\n;; pops regbank (caller-saved registers) off of 6502 stack\n.macro restore_regbank\n        .repeat 6, i\n        pla\n        sta regbank+5-i\n        .endrep\n.endmacro               ; restore_regbank\n\n;; sign-extends a into ax.  clobbers y.\n.macro signextend\n        tay\n        asl\n        lda #0\n        sbc #0\n        eor #$ff\n        tax\n        tya\n.endmacro               ; signextend\n\n        .code\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                             j65_init                             ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; void __fastcall__ j65_init(j65_state *s, void *ctx, j65_callback cb, uint8_t max_depth);\n.proc _j65_init\n        sta tmp1                ; save max_depth\n        lda state               ; save first 2 bytes of regbank\n        sta ptr1\n        lda state+1\n        sta ptr1+1\n\n        ldy #4                  ; get state pointer off stack\n        lda (sp),y\n        sta state\n        iny\n        lda (sp),y\n        sta state+1\n\n        ldy #.sizeof(st) - 1    ; clear state variables to 0\n        lda #0\nloop:   sta (state),y\n        dey\n        bpl loop\n\n        lda #par_done\n        putstate st::parser_st2\n        lda #$ff\n        putstate st::stack_idx\n        lda #0\n        sub tmp1                ; subtract max depth from 256\n        cmp #.sizeof(st)\n        bge depth_ok\n        lda #.sizeof(st)\ndepth_ok:\n        putstate st::stack_min\n\n        ldy #3                  ; copy callback and context from stack to state\nloop1:  lda (sp),y\n        sta (state),y\n        dey\n        bpl loop1\n\n        lda ptr1                ; restore first 2 bytes of regbank\n        sta state\n        lda ptr1+1\n        sta state+1\n        jmp incsp6              ; tail call to remove args from stack\n.endproc                ; _j65_init\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                             j65_parse                            ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; int8_t __fastcall__ j65_parse(j65_state *s, const char *buf, size_t len)\n.proc _j65_parse\n        sta jlen\n        stx jlen+1\n        save_regbank            ; save regbank onto 6502 stack\n        ldy #0                  ; move state and buf from C stack to regbank\n        lda (sp),y\n        sta inbuf\n        iny\n        lda (sp),y\n        sta inbuf+1\n        iny\n        lda (sp),y\n        sta state\n        sta strbuf\n        iny\n        lda (sp),y\n        sta state+1\n        add #1                  ; strbuf is state+256\n        sta strbuf+1\n        jsr incsp4              ; remove state and buf from C stack\nloop:   lda jlen+1\n        beq leftovers\n        pha                     ; save jlen on 6502 stack\n        lda jlen\n        pha\n        lda #$ff\n        sta inbuflast\n        jsr parse\n        tax\n        lda #$ff                ; increment file_off by 256\n        jsr add_a_plus_1_to_file_off\n        pla                     ; restore jlen off 6502 stack\n        sta jlen\n        pla\n        sub #1                  ; decrement jlen by 256\n        sta jlen+1\n        cpx #J65_WANT_MORE\n        bne done                ; error or success; don't need to parse more\n        inc inbuf+1             ; add 256 to inbuf\n        jmp loop\nleftovers:                      ; hi byte of jlen is zero\n        ldx jlen\n        beq done                ; if length was a multiple of 256\n        dex\n        stx inbuflast\n        txa\n        pha\n        jsr parse\n        tax\n        pla\n        jsr add_a_plus_1_to_file_off\ndone:   restore_regbank         ; restore regbank off of 6502 stack\n        txa                     ; return value\n        signextend\n        rts\n.endproc                ; _j65_parse\n\n;; adds a plus 1 to file_off.\n;; clobbers a and y.  preserves x.\n.proc add_a_plus_1_to_file_off\n        ldy #st::file_off\n        sec\n        adc (state),y           ; file_off\n        sta (state),y\n        iny\n        lda (state),y           ; file_off+1\n        adc #0\n        sta (state),y\n        iny\n        lda (state),y           ; file_off+2\n        adc #0\n        sta (state),y\n        iny\n        lda (state),y           ; file_off+3\n        adc #0\n        sta (state),y\n        rts\n.endproc                ; add_a_plus_1_to_file_off\n\n;; x will contain character, and a will contain character properties\n;; on exit. clobbers y.\n;; negative flag is set based on hi bit of properties.  (prop_ws)\n.proc getchar\n        ldy charidx\n        lda (inbuf),y\n        tax\n        bmi hiset\n        lda charprops,x\n        rts\nhiset:  lda #prop_str           ; non-ascii unicode char, legal only in strings\n        rts\n\n        .rodata\ncharprops:\n        .byte 0                          ; $00\n        .byte 0                          ; $01\n        .byte 0                          ; $02\n        .byte 0                          ; $03\n        .byte 0                          ; $04\n        .byte 0                          ; $05\n        .byte 0                          ; $06\n        .byte 0                          ; $07\n        .byte 0                          ; $08\n        .byte prop_ws                    ; $09\n        .byte prop_ws                    ; $0a\n        .byte 0                          ; $0b\n        .byte 0                          ; $0c\n        .byte prop_ws                    ; $0d\n        .byte 0                          ; $0e\n        .byte 0                          ; $0f\n        .byte 0                          ; $10\n        .byte 0                          ; $11\n        .byte 0                          ; $12\n        .byte 0                          ; $13\n        .byte 0                          ; $14\n        .byte 0                          ; $15\n        .byte 0                          ; $16\n        .byte 0                          ; $17\n        .byte 0                          ; $18\n        .byte 0                          ; $19\n        .byte 0                          ; $1a\n        .byte 0                          ; $1b\n        .byte 0                          ; $1c\n        .byte 0                          ; $1d\n        .byte 0                          ; $1e\n        .byte 0                          ; $1f\n        .byte prop_ws|prop_str           ; $20\n        .byte prop_str                   ; !\n        .byte prop_str|sc_quote          ; \"\n        .byte prop_str                   ; #\n        .byte prop_str                   ; $\n        .byte prop_str                   ; %\n        .byte prop_str                   ; &\n        .byte prop_str                   ; '\n        .byte prop_str                   ; (\n        .byte prop_str                   ; )\n        .byte prop_str                   ; *\n        .byte prop_str|prop_num          ; +\n        .byte prop_str|sc_comma          ; ,\n        .byte prop_str|prop_int|prop_num ; -\n        .byte prop_str|prop_num          ; .\n        .byte prop_str                   ; /\n        .byte prop_str|prop_int|prop_num ; 0\n        .byte prop_str|prop_int|prop_num ; 1\n        .byte prop_str|prop_int|prop_num ; 2\n        .byte prop_str|prop_int|prop_num ; 3\n        .byte prop_str|prop_int|prop_num ; 4\n        .byte prop_str|prop_int|prop_num ; 5\n        .byte prop_str|prop_int|prop_num ; 6\n        .byte prop_str|prop_int|prop_num ; 7\n        .byte prop_str|prop_int|prop_num ; 8\n        .byte prop_str|prop_int|prop_num ; 9\n        .byte prop_str|sc_colon          ; :\n        .byte prop_str                   ; ;\n        .byte prop_str                   ; <\n        .byte prop_str                   ; =\n        .byte prop_str                   ; >\n        .byte prop_str                   ; ?\n        .byte prop_str                   ; @\n        .byte prop_str                   ; A\n        .byte prop_str                   ; B\n        .byte prop_str                   ; C\n        .byte prop_str                   ; D\n        .byte prop_str|prop_num          ; E\n        .byte prop_str                   ; F\n        .byte prop_str                   ; G\n        .byte prop_str                   ; H\n        .byte prop_str                   ; I\n        .byte prop_str                   ; J\n        .byte prop_str                   ; K\n        .byte prop_str                   ; L\n        .byte prop_str                   ; M\n        .byte prop_str                   ; N\n        .byte prop_str                   ; O\n        .byte prop_str                   ; P\n        .byte prop_str                   ; Q\n        .byte prop_str                   ; R\n        .byte prop_str                   ; S\n        .byte prop_str                   ; T\n        .byte prop_str                   ; U\n        .byte prop_str                   ; V\n        .byte prop_str                   ; W\n        .byte prop_str                   ; X\n        .byte prop_str                   ; Y\n        .byte prop_str                   ; Z\n        .byte prop_str|sc_lsq            ; [\n        .byte prop_str                   ; \\\n        .byte prop_str|sc_rsq            ; ]\n        .byte prop_str                   ; ^\n        .byte prop_str                   ; _\n        .byte prop_str                   ; `\n        .byte prop_str|prop_lit          ; a\n        .byte prop_str                   ; b\n        .byte prop_str                   ; c\n        .byte prop_str                   ; d\n        .byte prop_str|prop_lit|prop_num ; e\n        .byte prop_str|prop_lit          ; f\n        .byte prop_str                   ; g\n        .byte prop_str                   ; h\n        .byte prop_str                   ; i\n        .byte prop_str                   ; j\n        .byte prop_str                   ; k\n        .byte prop_str|prop_lit          ; l\n        .byte prop_str                   ; m\n        .byte prop_str|prop_lit          ; n\n        .byte prop_str                   ; o\n        .byte prop_str                   ; p\n        .byte prop_str                   ; q\n        .byte prop_str|prop_lit          ; r\n        .byte prop_str|prop_lit          ; s\n        .byte prop_str|prop_lit          ; t\n        .byte prop_str|prop_lit          ; u\n        .byte prop_str                   ; v\n        .byte prop_str                   ; w\n        .byte prop_str                   ; x\n        .byte prop_str                   ; y\n        .byte prop_str                   ; z\n        .byte prop_str|sc_lcur           ; {\n        .byte prop_str                   ; |\n        .byte prop_str|sc_rcur           ; }\n        .byte prop_str                   ; ~\n        .byte prop_str                   ; $7f\n\n        .code\n.endproc                ; getchar\n\n;; state, strbuf, inbuf, and inbuflast should be set up upon entry.\n;; returns status in a.\n.proc parse\n        lda #0\n        sta charidx\nparseloop:\n        getstate st::lexer_st\n        tay\n        lda lex_tab_h,y\n        pha\n        lda lex_tab_l,y\n        pha\n        rts                     ; jump table; not end of subroutine\nl_ready:\n        jsr getchar\n        bmi got_whitespace\n        bit flags_prop_lit_or_num\n        bne start_lit\n        and #prop_sc\n        asl\n        asl\n        asl\n        sta tmp1\n        getstate st::parser_st\n        ora tmp1\n        tay\n        lda dispatch_tab_h,y\n        pha\n        lda dispatch_tab_l,y\n        pha\n        rts                     ; jump table; not end of subroutine\ngot_whitespace:\n        cpx #$0d\n        beq got_newline\n        cpx #$0a\n        bne jmp_nextchar\n        getstate st::prev_char\n        cmp #$0d\n        beq got_newline1        ; ignore LF if preceded by CR\ngot_newline:\n        ldy #st::line_num       ; increment line number\n        jsr inc_state_long\ngot_newline1:\n        lda #0                  ; set column number to 0\n        ldy #st::col_num\n        sta (state),y\n        iny\n        sta (state),y\n        iny\n        sta (state),y\n        iny\n        sta (state),y\n        sec                     ; add 1 in make_byte_offset\n        jsr make_byte_offset    ; get file offset+1 into regsave\n        ldy #st::line_off       ; move regsave into line offset\n        lda regsave\n        sta (state),y\n        iny\n        lda regsave+1\n        sta (state),y\n        iny\n        lda regsave+2\n        sta (state),y\n        iny\n        lda regsave+3\n        sta (state),y\n        jmp nextchar1\njmp_nextchar:\n        jmp nextchar\nstart_lit:\n        getstate st::parser_st\n        tay\n        lda literal_errors,y\n        bne error\n        lda #prop_lit | prop_int | prop_num\n        putstate st::flags\n        lda #0\n        putstate st::str_idx\n        lda #lex_literal\n        putstate st::lexer_st   ; fall thru and process same char as literal\nl_literal:\n        jsr getchar\n        ldy #st::flags\n        and (state),y\n        bne goodliteral\n        lda (state),y\n        tax\n        getstate st::str_idx\n        tay\n        lda #0\n        sta (strbuf),y          ; null-terminate string\n        txa\n        jsr handle_literal\n        bcs error\n        lda #lex_ready\n        putstate st::lexer_st\n        jmp parseloop           ; process the same character again\ngoodliteral:\n        sta (state),y           ; write back flags after and\n        jmp putchar\nl_string:\n        jsr getchar\n        and #prop_str\n        beq illegal_char\n        cpx #$5C                ; backslash\n        beq got_backslash\n        cpx #$22                ; double quote\n        beq got_quote\n        jmp putchar\ngot_backslash:\n        lda #lex_str_escape\n        putstate st::lexer_st\n        jmp nextchar\ngot_quote:\n        jsr handle_string\n        bcs error\n        lda #lex_ready\n        putstate st::lexer_st\n        jmp nextchar\nillegal_char:\n        lda #J65_ILLEGAL_CHAR\nerror:  rts                     ; error exit\nl_str_escape:\n        lda #lex_string\n        putstate st::lexer_st\n        ldy charidx             ; don't need to jsr getchar; don't need props\n        lda (inbuf),y\n        sta esc_code\n        cmp #$5c                ; backslash\n        beq escape_later\n        cmp #'u'\n        beq escape_later\n        jsr lookup_escape\n        bcs illegal_escape\n        tax\n        jmp putchar\nillegal_escape:\n        lda #J65_ILLEGAL_ESCAPE\n        rts                     ; error exit\nescape_later:\n        lda #1\n        putstate st::flags      ; flag indicating we need a later escape pass\n        getstate st::str_idx    ; re-insert backslash first\n        tay\n        lda #$5c                ; backslash\n        sta (strbuf),y\n        iny\n        beq strtoolong\n        lda esc_code\n        jmp putchar1\nputchar:                        ; x contains char to put in string buf\n        getstate st::str_idx\n        tay\n        txa\nputchar1:                       ; a contains char, y contains str_idx\n        sta (strbuf),y\n        iny\n        beq strtoolong\n        tya\n        putstate st::str_idx\nnextchar:\n        ldy #st::col_num\n        jsr inc_state_long\nnextchar1:\n        ldy charidx\n        lda (inbuf),y\n        putstate st::prev_char\n        getstate st::parser_st\n        cmp #par_done\n        beq done\n        lda charidx\n        cmp inbuflast\n        beq wantmore\n        inc charidx\n        jmp parseloop\nwantmore:\n        lda #J65_WANT_MORE\n        rts                     ; end of subroutine\ndone:   lda #J65_DONE\n        rts                     ; end of subroutine\nstrtoolong:\n        lda #J65_STRING_TOO_LONG\n        rts                     ; error exit\ndisp_illegal_char:\n        lda #J65_ILLEGAL_CHAR\n        rts                     ; error exit\ndisp_parse_error:\n        lda #J65_PARSE_ERROR\n        rts                     ; error exit\ndisp_exp_string:\n        lda #J65_EXPECTED_STRING\n        rts                     ; error exit\ndisp_exp_colon:\n        lda #J65_EXPECTED_COLON\n        rts                     ; error exit\ndisp_exp_comma:\n        lda #J65_EXPECTED_COMMA\n        rts                     ; error exit\ndisp_exp_obj_end:\n        lda #J65_EXPECTED_OBJ_END\n        rts                     ; error exit\ndisp_exp_array_end:\n        lda #J65_EXPECTED_ARRAY_END\nerror2: rts                     ; error exit\npop_and_error:\n        tax\n        pla\n        txa\n        rts                     ; error exit\ndisp_start_obj:\n        ldx #J65_START_OBJ\n        lda #0                  ; index into close_states\ndescend:\n        pha\n        stx evtype\n        getstate st::parser_st2\n        jsr push_state_stack\n        bcs pop_and_error\n        jsr call_callback\n        bcs pop_and_error\n        pla\n        tax\n        lda close_states,x\n        putstate st::parser_st\n        lda close_states+1,x\n        putstate st::parser_st2\n        jmp nextchar\ndisp_end_obj:\n        lda #J65_END_OBJ\nascend: sta evtype\n        jsr call_callback\n        bcs error2\n        jsr pop_state_stack\n        bcs error2\n        putstate st::parser_st\n        putstate st::parser_st2\n        jmp nextchar\ndisp_start_array:\n        ldx #J65_START_ARRAY\n        lda #2                  ; index into close_states\n        jmp descend\ndisp_end_array:\n        lda #J65_END_ARRAY\n        jmp ascend\ndisp_start_string:\n        lda #lex_string\n        putstate st::lexer_st\n        lda #0\n        putstate st::flags      ; boolean for second unescape pass\n        putstate st::str_idx\n        jmp nextchar\ndisp_comma_array:\n        lda #par_ready\ndca1:   putstate st::parser_st\n        jmp nextchar\ndisp_comma_object:\n        lda #par_key\n        jmp dca1\ndisp_colon:\n        lda #par_ready\n        jmp dca1\n\n        .rodata\n\n.define lex_tab l_ready-1, l_literal-1, l_string-1, l_str_escape-1\nlex_tab_l:\n        .lobytes lex_tab\nlex_tab_h:\n        .hibytes lex_tab\n.undefine lex_tab\n\nflags_prop_lit_or_num:\n        .byte prop_lit | prop_int | prop_num\n\n.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\n.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\n.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\n.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\n.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\n.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\n.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\n.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\ndispatch_tab_l:\n        .lobytes dt_none\n        .lobytes dt_lsq\n        .lobytes dt_lcur\n        .lobytes dt_rsq\n        .lobytes dt_rcur\n        .lobytes dt_colon\n        .lobytes dt_comma\n        .lobytes dt_quote\ndispatch_tab_h:\n        .hibytes dt_none\n        .hibytes dt_lsq\n        .hibytes dt_lcur\n        .hibytes dt_rsq\n        .hibytes dt_rcur\n        .hibytes dt_colon\n        .hibytes dt_comma\n        .hibytes dt_quote\n\n.undefine dt_none\n.undefine dt_lsq\n.undefine dt_lcur\n.undefine dt_rsq\n.undefine dt_rcur\n.undefine dt_colon\n.undefine dt_comma\n.undefine dt_quote\n\nclose_states:\n        .byte par_key_or_close_object, par_need_comma_or_close_object\n        .byte par_ready_or_close_array, par_need_comma_or_close_array\n\nliteral_errors:                 ; needs to match parser state enum\n        .byte 0, 0, J65_EXPECTED_STRING, J65_EXPECTED_STRING, J65_EXPECTED_COLON\n        .byte J65_EXPECTED_COMMA, J65_EXPECTED_COMMA, J65_PARSE_ERROR\n\n        .code\n\n.endproc                ; parse\n\n;; event type is in evtype.\n;; Returns callback's return value in a.\n;; Sets carry if return value is negative.\n;; clobbers all regs.\n.proc call_callback\n        lda inbuflast           ; save caller-save regs\n        pha\n        lda charidx\n        pha\n\n        ldy #st::callback       ; get callback into ptr4\n        lda (state),y\n        sta ptr4\n        iny\n        lda (state),y\n        sta ptr4+1\n\n        lda state               ; state ptr is passed on stack\n        ldx state+1\n        jsr pushax\n\n        lda evtype              ; event type in ax\n        ldx #0\n        jsr callptr4            ; call the C callback function (in ptr4)\n\n        tax                     ; save return value\n        pla                     ; restore caller-save regs\n        sta charidx\n        pla\n        sta inbuflast\n        txa\n        asl                     ; set carry if return value is negative\n        txa                     ; get return value back into a\n        rts                     ; end of subroutine\n.endproc                ; call_callback\n\n;; add charidx plus carry flag to file_off and store result in regsave.\n;; clobbers a, x, y.\n.proc make_byte_offset\n        lda charidx\n        ldy #st::file_off\n        adc (state),y\n        sta regsave\n        lda #0\n        iny\n        adc (state),y\n        sta regsave+1\n        iny\n        lda #0\n        adc (state),y\n        sta regsave+2\n        iny\n        lda #0\n        adc (state),y\n        sta regsave+3\n        rts\n.endproc                ; make_byte_offset\n\n;; Takes escape code in esc_code (tmp2).\n;; If legal, returns escaped char in a with carry clear.\n;; If not legal, returns with carry set.\n;; Clobbers y, preserves x.\n.proc lookup_escape\n        ldy #0\nloop:   lda escape_codes,y\n        beq notfound\n        iny\n        cmp esc_code\n        bne loop\n        dey\n        lda escaped_chars,y\n        clc\n        rts\nnotfound:\n        sec\n        rts\n\n        .rodata\nescape_codes:\n        .byte $22,\"/bfnrt\",0\nescaped_chars:\n        .byte $22, $2f, $08, $0c, $0a, $0d, $09\n        .code\n\n.endproc                ; lookup_escape\n\n;; Handle a double-quoted string.\n;; Check flags to see if it needs a second unescaping pass.\n;; Clobbers all registers.\n;; On success, returns carry clear.\n;; On error, returns carry set with error event in a.\n.proc handle_string\n        getstate st::flags\n        beq skipescape\n        jsr unescape_unicode\n        bcc skipescape\n        lda #J65_ILLEGAL_ESCAPE\n        rts                     ; error exit; carry is still set\nskipescape:\n        getstate st::str_idx\n        tay\n        lda #0\n        sta (strbuf),y          ; null-terminate string\n        getstate st::parser_st\n        cmp #par_ready\n        beq p_ready\n        cmp #par_ready_or_close_array\n        beq p_ready\n        cmp #par_key_or_close_object\n        beq p_key\n        cmp #par_key\n        beq p_key\n        lda #J65_PARSE_ERROR\n        sec\nerror:  rts                     ; error exit\np_ready:\n        lda #J65_STRING\n        sta evtype\n        jsr call_callback\n        bcs error\n        getstate st::parser_st2 ; get next parser state in a\n        putstate st::parser_st\n        clc\n        rts                     ; success exit\np_key:  lda #J65_KEY\n        sta evtype\n        jsr call_callback\n        bcs error\n        lda #par_need_colon\n        putstate st::parser_st\n        clc\n        rts                     ; success exit\n.endproc                ; handle_string\n\n;; unescape \\\\ and \\u in the string buffer.\n;; returns carry set on error.  clear on success.\n;; clobbers all registers.\n.proc unescape_unicode\n        getstate st::str_idx\n        sta tmp2\n        ldy #0\n        sty tmp1\nloop:   cpy tmp2\n        beq done\n        lda (strbuf),y\n        iny\n        cmp #$5c                ; backslash\n        beq escape\nloop1:  sty tmp5\n        ldy tmp1\n        sta (strbuf),y\n        inc tmp1\n        ldy tmp5\n        jmp loop\nescape: cpy tmp2\n        beq error\n        lda (strbuf),y\n        iny\n        cmp #$5c                ; backslash\n        beq loop1\n        cmp #'u'\n        beq unicode\nerror:  sec\n        rts\nunicode:\n        jsr read4hexintosreg\n        bcs error\n        jsr movesregtolong1\n        lda (strbuf),y\n        cpy tmp2\n        beq bmp\n        cmp #$5c                ; backslash\n        bne bmp\n        iny\n        cpy tmp2\n        beq bmp0\n        lda (strbuf),y\n        cmp #'u'\n        beq check_surrogate\nbmp0:   dey\nbmp:    jsr long1toutf8\n        jmp loop\nbmp1:   pla\n        tay\n        jmp bmp\ncheck_surrogate:\n        jsr is_sreg_left_surrogate\n        bcc bmp0\n        tya\n        sub #1\n        pha\n        iny\n        jsr read4hexintosreg\n        bcs bmp1\n        jsr is_sreg_right_surrogate\n        bcc bmp1\n        pla\n        jsr combine_surrogates\n        jmp bmp\ndone:   lda tmp1\n        putstate st::str_idx\n        clc\n        rts\n.endproc                ; unescape_unicode\n\n;; reads 4 hex digs from strbuf at y into sreg.\n;; (buffer length is in tmp2)\n;; on success, carry clear, leaves y pointing after 4 digs.\n;; on failure, carry set.\n.proc read4hexintosreg\n        ldx #4\nloop:   jsr shift_sreg_left_4bits\n        jsr or1hexintosreg\n        bcs done\n        dex\n        bne loop\n        clc\ndone:   rts\n.endproc                ; read4hexintosreg\n\n;; clobbers a, preserves x and y.\n.proc shift_sreg_left_4bits\n        lda sreg\n        asl a\n        rol sreg+1\n        asl a\n        rol sreg+1\n        asl a\n        rol sreg+1\n        asl a\n        rol sreg+1\n        sta sreg\n        rts\n.endproc                ; shift_sreg_left_4bits\n\n;; reads 1 hex dig from strbuf at y into low 4 bits of sreg.\n;; (buffer length is in tmp2)\n;; on success, carry clear, leaves y pointing after digit.\n;; on failure, carry set.\n.proc or1hexintosreg\n        cpy tmp2\n        beq fail\n        lda (strbuf),y\n        iny\n        jsr hex_dig_to_nibble\n        bcs fail\n        ora sreg\n        sta sreg\n        clc\n        rts\nfail:   sec\n        rts\n.endproc                ; or1hexintosreg\n\n;; converts ascii char in a to nibble in a.\n;; sets carry if not a hex digit.\n;; preserves x and y.\n.proc hex_dig_to_nibble\n        cmp #'0'\n        blt fail\n        cmp #'9'+1\n        bge tryupper\n        sub #'0'\n        clc\n        rts\ntryupper:\n        cmp #'A'\n        blt fail\n        cmp #'F'+1\n        bge trylower\n        sub #'A'-10\n        clc\n        rts\ntrylower:\n        cmp #'a'\n        blt fail\n        cmp #'f'+1\n        bge fail\n        sub #'a'-10\n        clc\n        rts\nfail:   sec\n        rts\n.endproc                ; hex_dig_to_nibble\n\n;; zero-extends sreg into long1.\n;; clobbers a, preserves x and y\n.proc movesregtolong1\n        lda sreg\n        sta long1\n        lda sreg+1\n        sta long1+1\n        lda #0\n        sta long1+2\n        sta long1+3\n        rts\n.endproc                ; movesregtolong1\n\n;; converts long1 to utf8 in strbuf at tmp1.\n;; (output index is in tmp1)\n;; preserves y.\n.proc long1toutf8\n        sty tmp5\n        ldy tmp1\n        lda long1+2\n        bne len4\n        lda long1+1\n        beq latin1\n        cmp #8\n        bge len3\nlen2:   ldx #1\n        jsr utf8_shift\n        lda long1\n        and #%00111111\n        ora #%10000000\n        sta long1\n        lda long1+1\n        and #%00011111\n        ora #%11000000\n        sta long1+1\n        ldx #1\n        jmp done\nlen3:   ldx #1\n        jsr utf8_shift\n        ldx #2\n        jsr utf8_shift\n        lda long1\n        and #%00111111\n        ora #%10000000\n        sta long1\n        lda long1+1\n        and #%00111111\n        ora #%10000000\n        sta long1+1\n        lda long1+2\n        and #%00001111\n        ora #%11100000\n        sta long1+2\n        ldx #2\n        jmp done\nlen4:   ldx #1\n        jsr utf8_shift\n        ldx #2\n        jsr utf8_shift\n        ldx #3\n        jsr utf8_shift\n        lda long1\n        and #%00111111\n        ora #%10000000\n        sta long1\n        lda long1+1\n        and #%00111111\n        ora #%10000000\n        sta long1+1\n        lda long1+2\n        and #%00111111\n        ora #%10000000\n        sta long1+2\n        lda long1+3\n        and #%00000111\n        ora #%11110000\n        sta long1+3\n        ldx #3\n        jmp done\nlatin1: lda long1\n        bmi len2\n        ldx #0                  ; length 1, already in the right format\ndone:   jsr writeutf8\n        sty tmp1\n        ldy tmp5\n        rts\n.endproc                ; long1toutf8\n\n;; writes the first x+1 bytes of long1, in reverse order,\n;; to strbuf, starting at y.  advances y.\n;; clobbers a, x.\n.proc writeutf8\n        lda long1,x\n        sta (strbuf),y\n        iny\n        dex\n        bpl writeutf8\n        rts\n.endproc                ; writeutf8\n\n;; shift the last 4-x bytes of long1 left by 2 bits.\n;; clobbers a, x.  preserves y.\n.proc shift_left_by_2\n        stx tmp6\n        jsr shift_left_by_1\n        ldx tmp6\nshift_left_by_1:\n        asl long1,x\nloop:   php\n        cpx #3\n        beq done\n        inx\n        plp\n        rol long1,x\n        jmp loop\ndone:   plp\n        rts\n.endproc                ; shift_left_by_2\n\n;; shift the last 4-x bytes of long1 left by 2 bits.\n;; shifts in the top two bits from the previous byte, too.\n;; clobbers a, x.  preserves y.\n.proc utf8_shift\n        dex\n        txa\n        pha\n        jsr shift_left_by_2\n        pla\n        tax\n        lsr long1,x\n        lsr long1,x\n        rts\n.endproc                ; utf8_shift\n\n;; preserves y.\n;; sets carry if sreg is a left surrogate.\n.proc is_sreg_left_surrogate\n        lda sreg+1\n        and #$fc\n        cmp #$d8\n        beq yes\n        clc\n        rts\nyes:    sec\n        rts\n.endproc                ; is_sreg_left_surrogate\n\n;; preserves y.\n;; sets carry if sreg is a right surrogate.\n.proc is_sreg_right_surrogate\n        lda sreg+1\n        and #$fc\n        cmp #$dc\n        beq yes\n        clc\n        rts\nyes:    sec\n        rts\n.endproc                ; is_sreg_right_surrogate\n\n;; combine left surrogate in long1 with right surrogate in sreg.\n;; result in long1.  preserves y.\n.proc combine_surrogates\n        lda long1+1\n        and #3\n        sta long1+2\n        lda long1\n        sta long1+1\n        asl long1+1\n        rol long1+2\n        asl long1+1\n        rol long1+2\n        lda sreg\n        sta long1\n        lda sreg+1\n        and #3\n        ora long1+1\n        sta long1+1\n        inc long1+2\n        rts\n.endproc                ; combine_surrogates\n\n;; parse signed integer in strbuf (length in str_idx).\n;; on success, carry clear and result in long1 (regsave).\n;; on integer overflow, carry set and overflow set.\n;; on illegal character, carry set and overflow clear.\n;; clobbers a, x, and y.\n.proc parse_signed_integer\n        ldy #0\n        lda (strbuf),y\n        cmp #'-'\n        beq negative\n        jsr parse_unsigned_integer\n        bcs done                ; overflow of 32-bit int, C and V are set\n        bit long1+3\n        bpl done                ; if hi bit is clear, it is okay\nnot_okay:\n        sec                     ; otherwise, set carry and overflow\n        bit an_rts\ndone:\nan_rts: rts\nnegative:\n        iny\n        jsr parse_unsigned_integer\n        bcs done                ; overflow of 32-bit int, C and V are set\n        lda #$7f\n        bit long1+3\n        bpl okay                ; if hi bit is clear, it is okay\n        bne not_okay            ; if hi byte is not $80, it is not okay\n        lda long1+2\n        ora long1+1\n        ora long1\n        bne not_okay            ; only okay if 3 least significant bytes are 0\nokay:   jsr resteax\n        jsr negeax\n        jsr saveeax\n        clc\n        jmp done\n.endproc                ; parse_signed_integer\n\n;; parse unsigned integer in strbuf, starting at y.\n;; on success, carry clear and result in long1 (regsave).\n;; on integer overflow, carry set and overflow set.\n;; on illegal character, carry set and overflow clear.\n;; clobbers a and y.  preserves x.\n.proc parse_unsigned_integer\n        lda #0\n        sta long1\n        sta long1+1\n        sta long1+2\n        sta long1+3\nloop:   tya\n        ldy #st::str_idx\n        cmp (state),y\n        bge done\n        tay\n        jsr multiply_long1_by_10\n        bcs overflow\n        lda (strbuf),y\n        jsr hex_dig_to_nibble\n        bcs error\n        jsr add_a_to_long1\n        iny\n        bcc loop\noverflow:\n        bit an_rts              ; bit on an rts instruction will set overflow\nan_rts: rts                     ; carry and overflow are both set\ndone:   clc\n        rts                     ; success: carry clear\nerror:  clv\n        sec\n        rts                     ; carry set, overflow clear\n.endproc                ; parse_unsigned_integer\n\n;; multiplies long1 by 10. clobbers a and long2. preserves x y.\n;; returns with carry set if result overflows a 32-bit unsigned long.\n.proc multiply_long1_by_10\n        jsr shift_long1_left_by_1\n        bcs done\n        lda long1\n        sta long2\n        lda long1+1\n        sta long2+1\n        lda long1+2\n        sta long2+2\n        lda long1+3\n        sta long2+3\n        jsr shift_long1_left_by_1\n        bcs done\n        jsr shift_long1_left_by_1\n        bcs done\n        lda long1\n        add long2\n        sta long1\n        lda long1+1\n        adc long2+1\n        sta long1+1\n        lda long1+2\n        adc long2+2\n        sta long1+2\n        lda long1+3\n        adc long2+3\n        sta long1+3\ndone:   rts\n.endproc                ; multiply_long1_by_10\n\n;; shifts long1 left by 1.  clobbers a; preserves x and y.\n.proc shift_long1_left_by_1\n        asl long1\n        rol long1+1\n        rol long1+2\n        rol long1+3\n        rts\n.endproc                ; shift_long1_left_by_1\n\n;; add a to long1.  sets carry if result overflows an unsigned long.\n;; clobbers a; preserves x and y.\n.proc add_a_to_long1\n        add long1\n        sta long1\n        lda long1+1\n        adc #0\n        sta long1+1\n        lda long1+2\n        adc #0\n        sta long1+2\n        lda long1+3\n        adc #0\n        sta long1+3\n        rts\n.endproc                ; add_a_to_long1\n\n;; parse \"null\", \"false\", or \"true\" in strbuf (length in str_idx).\n;; on success, carry clear and a contains event number.\n;; on failure, carry set.  clobbers x and y.\n.proc identify_literal\n        getstate st::str_idx\n        cmp #4\n        beq len4\n        cmp #5\n        beq len5\nfail:   sec\n        rts\nlen4:   lda #<str_null\n        ldx #>str_null\n        ldy #3\n        jsr compare_strings\n        beq got_null\n        lda #<str_true\n        ldx #>str_true\n        ldy #3\n        jsr compare_strings\n        bne fail\n        lda #J65_TRUE\n        clc\n        rts\ngot_null:\n        lda #J65_NULL\n        clc\n        rts\nlen5:   lda #<str_false\n        ldx #>str_false\n        ldy #4\n        jsr compare_strings\n        bne fail\n        lda #J65_FALSE\n        clc\n        rts\n\n        .rodata\nstr_null:\n        .byte \"null\"\nstr_true:\n        .byte \"true\"\nstr_false:\n        .byte \"false\"\n        .code\n\n.endproc                ; identify_literal\n\n;; compares string (of length y+1) at strbuf with string pointed to\n;; by a (lo byte) and x (hi byte).\n;; returns with zero flag set if strings match, or zero flag clear\n;; if they do not.\n;; clobbers ptr1.\n.proc compare_strings\n        sta ptr1\n        stx ptr1+1\nloop:   lda (strbuf),y\n        cmp (ptr1),y\n        bne done\n        dey\n        bpl loop\n        lda #0                  ; set zero flag\ndone:   rts\n.endproc                ; compare_strings\n\n;; Handle a literal (a number, or null, true, or false).\n;; On entry, a should contain flags.  (prop_lit, prop_int, prop_num)\n;; Clobbers all registers.\n;; On success, returns carry clear.\n;; On error, returns carry set with error event in a.\n.proc handle_literal\n        tax\n        getstate st::parser_st\n        cmp #par_ready\n        beq p_ready\n        cmp #par_ready_or_close_array\n        beq p_ready\nparse_err:\n        lda #J65_PARSE_ERROR\n        sec\nerror:  rts                     ; error exit\np_ready:\n        txa\n        bit flags_prop_lit\n        bne keyword\n        bit flags_prop_int\n        bne integer\nnumber: lda #J65_NUMBER\ndo_callback:\n        sta evtype\n        jsr call_callback\n        bcs error\n        getstate st::parser_st2 ; get next parser state in a\n        putstate st::parser_st\n        clc\n        rts                     ; success exit\ninteger:\n        jsr parse_signed_integer\n        bcs not_integer\n        ldy #st::long_val       ; copy long1 to long_val\n        lda long1\n        sta (state),y\n        iny\n        lda long1+1\n        sta (state),y\n        iny\n        lda long1+2\n        sta (state),y\n        iny\n        lda long1+3\n        sta (state),y\n        lda #J65_INTEGER\n        jmp do_callback\nnot_integer:\n        bvs number\n        jmp parse_err\nkeyword:\n        jsr identify_literal\n        bcs parse_err\n        jmp do_callback\n\n        .rodata\nflags_prop_lit:\n        .byte prop_lit\nflags_prop_int:\n        .byte prop_int\n        .code\n\n.endproc                ; handle_literal\n\n;; push a onto the state stack.\n;; carry clear on success.\n;; carry set on error, with error event in a.\n;; clobbers x, y.\n.proc push_state_stack\n        tax\n        getstate st::stack_idx\n        ldy #st::stack_min\n        cmp (state),y\n        blt stack_full\n        tay\n        txa\n        sta (state),y\n        dey\n        tya\n        putstate st::stack_idx\n        clc\n        rts\nstack_full:\n        lda #J65_NESTING_TOO_DEEP\n        sec\n        rts\n.endproc                ; push_state_stack\n\n;; pop the state stack.\n;; carry clear on success, with popped state in a.\n;; carry set on error, with error event in a.\n;; clobbers x, y.\n.proc pop_state_stack\n        getstate st::stack_idx\n        tay\n        iny\n        beq stack_empty\n        lda (state),y\n        tax\n        tya\n        putstate st::stack_idx\n        txa\n        clc\n        rts\nstack_empty:\n        lda #J65_PARSE_ERROR\n        sec\n        rts\n.endproc                ; pop_state_stack\n\n;; increment the long at state+y to state+y+3 by 1.\n;; clobbers a and y.\n.proc inc_state_long\n        lda (state),y\n        add #1\n        sta (state),y\n        bcc done                ; short circuit for speed\n        iny\n        lda (state),y\n        adc #0\n        sta (state),y\n        iny\n        lda (state),y\n        adc #0\n        sta (state),y\n        iny\n        lda (state),y\n        adc #0\n        sta (state),y\ndone:   rts\n.endproc                ; inc_state_long\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                          j65_get_string                          ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; const char * __fastcall__ j65_get_string(const j65_state *s);\n;; (string buffer is the second 256 bytes of state, so all we have\n;; to do is increment the high byte of the argument)\n.proc _j65_get_string\n        inx\n        rts\n.endproc                ; _j65_get_string\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                          j65_get_length                          ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; uint8_t __fastcall__ j65_get_length(const j65_state *s);\n.proc _j65_get_length\n        sta ptr1\n        stx ptr1+1\n        ldy #st::str_idx\n        lda (ptr1),y\n        ldx #0\n        rts\n.endproc                ; _j65_get_length\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                          j65_get_integer                         ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; int32_t __fastcall__ j65_get_integer(const j65_state *s);\n_j65_get_integer:\n        ldy #st::long_val+3\n;; copies the value at ax+y-3 thru ax+y to eax\nget_long:\n        sta ptr1\n        stx ptr1+1\n        lda (ptr1),y\n        sta sreg+1\n        dey\n        lda (ptr1),y\n        sta sreg\n        dey\n        lda (ptr1),y\n        tax\n        dey\n        lda (ptr1),y\n        rts\n        ; end _j65_get_long\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                        j65_get_line_offset                       ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; uint32_t __fastcall__ j65_get_line_offset(const j65_state *s);\n.proc _j65_get_line_offset\n        ldy #st::line_off+3\n        jmp get_long\n.endproc                ; _j65_get_line_offset\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                        j65_get_line_number                       ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; uint32_t __fastcall__ j65_get_line_number(const j65_state *s);\n.proc _j65_get_line_number\n        ldy #st::line_num+3\n        jmp get_long\n.endproc                ; _j65_get_line_number\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                       j65_get_column_number                      ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; uint32_t __fastcall__ j65_get_column_number(const j65_state *s);\n.proc _j65_get_column_number\n        ldy #st::col_num+3\n        jmp get_long\n.endproc                ; _j65_get_column_number\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                       j65_get_current_depth                      ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; uint8_t __fastcall__ j65_get_current_depth(const j65_state *s);\n.proc _j65_get_current_depth\n        ldy #st::stack_idx\n        sta ptr1\n        stx ptr1+1\n        lda #255\n        sub (ptr1),y\n        ldx #0\n        rts\n.endproc                ; _j65_get_current_depth\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                         j65_get_max_depth                        ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; uint8_t __fastcall__ j65_get_max_depth(const j65_state *s);\n.proc _j65_get_max_depth\n        ldy #st::stack_min\n        sta ptr1\n        stx ptr1+1\n        lda #0\n        sub (ptr1),y\n        ldx #0\n        rts\n.endproc                ; _j65_get_max_depth\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;                          j65_get_context                         ;;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n;; void * __fastcall__ j65_get_context(const j65_state *s);\n.proc _j65_get_context\n        ldy #st::context+1\n        sta ptr1\n        stx ptr1+1\n        lda (ptr1),y\n        tax\n        dey\n        lda (ptr1),y\n        rts\n.endproc                ; _j65_get_context\n"
  },
  {
    "path": "tests/create-testfile-disk-image.pl",
    "content": "#!/usr/bin/perl -w\n\n# JSON65 - A JSON parser for the 6502 microprocessor.\n#\n# https://github.com/ppelleti/json65\n#\n# Copyright © 2018 Patrick Pelletier\n#\n# This software is provided 'as-is', without any express or implied\n# warranty.  In no event will the authors be held liable for any damages\n# arising from the use of this software.\n#\n# Permission is granted to anyone to use this software for any purpose,\n# including commercial applications, and to alter it and redistribute it\n# freely, subject to the following restrictions:\n#\n# 1. The origin of this software must not be misrepresented; you must not\n#    claim that you wrote the original software. If you use this software\n#    in a product, an acknowledgment in the product documentation would be\n#    appreciated but is not required.\n# 2. Altered source versions must be plainly marked as such, and must not be\n#    misrepresented as being the original software.\n# 3. This notice may not be removed or altered from any source distribution.\n\nuse strict;\nuse FindBin;\n\n# Assumes that the \"ac\" shell script wrapper for Apple Commander\n# is on your PATH:\n# https://applecommander.github.io/install/\n\nmy $ac = \"ac\";\n\nmy $diskimage = \"testfile.po\";\n\nmy $tests = $FindBin::Bin;\nchdir ($tests);\n\nmy $blue = \"\\e[34m\";\nmy $green = \"\\e[32m\";\nmy $red = \"\\e[31m\";\nmy $off = \"\\e[0m\";\n\nsub mysystem {\n    my @cmd = @_;\n    print join(\" \", @cmd), \"\\n\";\n    if (system (@cmd) != 0) {\n        if ($? == -1) {\n            die \"$red*** fatal: $!$off\\n\";\n        } elsif ($? & 127) {\n            die (sprintf (\"$red*** fatal: signal %d$off\\n\", $? & 127));\n        } else {\n            die (sprintf (\"$red*** fatal: exit code %d$off\\n\", $? >> 8));\n        }\n    }\n}\n\nsub print_heading {\n    my $str = $_[0];\n    print \"$blue*** $str$off\\n\";\n}\n\nprint_heading \"Creating disk image\";\nmysystem (\"rm\", \"-f\", $diskimage);\nmysystem ($ac, \"-pro140\", $diskimage, \"testfile\");\n\nprint_heading \"Adding test program\";\nmy $program = \"testfile.system\";\nmysystem (\"$ac -as $diskimage $program < $program\");\n\nprint_heading \"Adding JSON files\";\nmy @json = split (' ', `echo file??.json`);\nforeach my $json (@json) {\n    mysystem (\"$ac -p $diskimage $json TXT < $json\");\n}\n"
  },
  {
    "path": "tests/file00.json",
    "content": "{ \"hello\", \"error\" }\n"
  },
  {
    "path": "tests/file01.json",
    "content": "{ \"mismatched\": true ]\n"
  },
  {
    "path": "tests/file02.json",
    "content": "{ null: \"bad\" }\n"
  },
  {
    "path": "tests/file03.json",
    "content": "{ \"error\": $foo }\n"
  },
  {
    "path": "tests/file04.json",
    "content": "[[[[[[[[[[[[[[[[[\"nesting\"]]]]]]]]]]]]]]]]]\n"
  },
  {
    "path": "tests/file05.json",
    "content": "{\n    \"multiple\": 1,\n    \"lines\": 2,\n    \"trouble\":: 3\n}\n"
  },
  {
    "path": "tests/file06.json",
    "content": "[\n    \"an\",\n    \"extra\",\n    \"comma\",\n]\n"
  },
  {
    "path": "tests/file07.json",
    "content": "[ \"a string which is way too long: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?!!?ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210 :gnol oot yaw si hcihw gnirts a\" ]\n"
  },
  {
    "path": "tests/test-file.c",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#include <conio.h>\n\n#include \"json65-file.h\"\n\nstatic uint8_t scratch[1024];\nstatic char filename[80];\n\nstatic void errfunc (FILE *err, void *ctx, int8_t status) {\n    fprintf (err, \"Unknown error %d\\n\", status);\n}\n\nstatic int8_t callback (j65_parser *p, uint8_t event) {\n    return 0;\n}\n\nint main (int argc, char **argv) {\n    FILE *f;\n    int8_t status;\n    uint8_t width, height, i;\n\n    screensize (&width, &height);\n\n    for (i = 0 ; i < 100 ; i++) {\n        snprintf (filename, sizeof (filename), \"file%02d.json\", i);\n\n        printf (\"\\nParsing %s\\n\\n\", filename);\n\n        f = fopen (filename, \"r\");\n        if (! f)\n            break;\n\n        status = j65_parse_file (f, scratch, sizeof (scratch),\n                                 NULL, callback, 16,\n                                 stderr, width, filename, errfunc);\n\n        fclose (f);\n\n        cgetc();\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/test-print.c",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#include <string.h>\n#include \"json65-print.h\"\n\nstatic char buf1[2048];\nstatic char buf2[2048];\nstatic j65_parser parser;\nstatic j65_tree tree;\nstatic const char infile[] = \"test-print.json\";\nstatic const char outfile[] = \"json.test.print.tmp\";\n\nstatic int do_test (void) {\n    int8_t status;\n    size_t len;\n    FILE *f;\n    int ret;\n\n    j65_init_tree (&tree);\n    j65_init (&parser, &tree, j65_tree_callback, 255);\n    len = strlen (buf1);\n    status = j65_parse (&parser, buf1, len);\n    if (status != J65_DONE) {\n        fprintf (stderr, \"status %d\\n\", status);\n        return 1;\n    }\n\n    f = fopen (outfile, \"w\");\n    if (! f) {\n        fprintf (stderr, \"Couldn't open file '%s' for writing\\n\", outfile);\n        return 1;\n    }\n\n    ret = j65_print_tree (tree.root, f);\n    fputc ('\\n', f);\n    fclose (f);\n\n    if (ret < 0) {\n        fprintf (stderr, \"Error writing file\\n\");\n        return 1;\n    }\n\n    f = fopen (outfile, \"r\");\n    if (! f) {\n        fprintf (stderr, \"Couldn't open file '%s' for reading\\n\", outfile);\n        return 1;\n    }\n\n    if (! fgets (buf2, sizeof (buf2), f)) {\n        fprintf (stderr, \"Couldn't read file\\n\");\n        return 1;\n    }\n\n    fclose (f);\n\n    if (0 != strcmp (buf1, buf2)) {\n        fprintf (stderr, \"strings not equal:\\n%s%s\\n\", buf1, buf2);\n        return 1;\n    }\n\n    j65_free_tree (&tree);\n\n    return 0;\n}\n\nint main (int argc, char **argv) {\n    FILE *f;\n    int badness = 0;\n\n    f = fopen (infile, \"r\");\n    if (! f) {\n        fprintf (stderr, \"Couldn't open file '%s' for reading\\n\", infile);\n        return 1;\n    }\n\n    while (fgets (buf1, sizeof (buf1), f)) {\n        badness += do_test ();\n    }\n\n    fclose (f);\n\n    if (badness == 0)\n        fprintf (stderr, \"Success!\\n\");\n\n    return badness;\n}\n"
  },
  {
    "path": "tests/test-print.json",
    "content": "[\"Hello\",\"World!\"]\n{\"foo\":\"bar\",\"nested\":[1,2,3]}\n\"Just a string\"\n5\nfalse\n[0.32572436,true,-11186217319148845949,true,0.27938694,0.75560504]\n[]\n{\"$\":1.8247128e-2,\"9Sz2D\\r\":12737293809857351644,\"\\u0016c\\u0004\\f\":false}\n[\"\\u0002=\\\"{\",null,\"pb\\u0005\",\"\\u0011r't\",9.4976306e-2,\"'\"]\n{\"\":7603252728548869453,\"{\\u001e\":\"\\n\",\"<\\u001536%E\":274716178498089137,\"&lB\":false,\"%(v\\u001f\\u0001t\":-13982809748860309521}\n{\"c+F![\":\"\",\"\\u0016\\n\\u0001\\u0004UB\":\"<\",\"\":4.665512e-2,\")\\nX3\\u0019\":\"\",\"EK%\":\"\\u0011\\u0013\\u0014\",\"vg\\u00047m\":false}\n{\"odG\\u0013C\":0.44743448,\"@nuwx\":false,\"S)5aT\":\"\",\"}\\u0004\\u001fa\":-12152612225799926450,\"\\f0m\\u0006\":-10691034618040127166}\n[0.5863078,\"OJ\",0.15542328]\n[\"Du\\u0014\",8827766992909995585,0.2134161,0.90028864,\"C\"]\n[\"4t4(w\",\"+BPq\",false,45313040627093027]\n[\"\\u0018Q~F\",0.33080858,\"\\u001cNu9 \",\"f\\u001a5H2R\",1707525377738296452,0.924928,true,false,false,true,0.9340122,0.5125267]\n[\"Q4\\u0004\",null,\"Hw\",\"h\",\"\",-3312958782175383110,false,\"`V0u~t\",-17853009426579656790,true,\"\\u0012\\u001fKe5\\u0001\",null]\n{\"\\t\\u0001FK\\u0010\\u0012\":\"E\"}\n[true,3213833407962205374,false,0.9740944,null,0.69657785,-12761276671025544045,false,4932447720730719695,2646858136238674420,225658451089210750]\n[10790624673763905503,12435287114942852151,-7403136589746519035,14505181554639941694,\"'u\"]\n{}\n{\"O\\u000e\\u0019t\\u0018c\":-3460634944561346323,\"'yXb\\u001e\":false,\"=#+Q\":0.11444086,\"\\u0015F\\u0006\":false,\"<gZP\\u001c\\u001f\":-8960435077329510243,\"szu\":0.6044294}\n{\"&.\\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}\n{\"*\":0.8523169,\"\\u0010,\":false,\"\\u00079\":0.8049666}\n[-5327988121100041754,false,-13018219928679153234,-2331534658977772737,0.78117466,\"#a\\u00190'\",\"\",false,4.1714847e-2,0.56394154]\n{\"\":-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}\n[-12036274170737061087,\"G,Xc~w\",\"l\\b\",false,true,-14114159388375863713,-17049232511652779296]\n[\"T\",\"\",4.8560143e-2,10885120778919441467,0.28279763,15914637999585694553,9020298222284681635,false,\"\\rw+m\",null]\n[-17968810982863699742,false,null]\n{\"j\\u000b7(=\":\"d\\u000beSa\",\"\\u001fn=8^|\":\"\"}\n{\"\":-4299274683684051628,\"\":0.89675933}\n[0.69776416,6.477058e-2,0.6377239,4.8692107e-2,true]\n[\"B_Xm7\",0.64527464,[],null,{\"\":0.60033214,\"\\u000bj:\\u000b/\":-17464052542706863991,\"\\u0014&cI\\u0012+\":[],\"P\":[]}]\n[false,8.072078e-3,0.7576571,null,[\"\\u0004</e:\",\"f;5\\b]\",0.13011688,\"-\\u0016{L^r\"]]\n[false,\"*Z\"]\n{\"\":-14448800448673676895,\"@y\\u001aq}\\u0002\":0.11866385,\"K4V;\\u0014\":[[\"w\",0.2579152,\"M\",\"\",8313590187149240626],\"$>h\",[\"X\",0.45122665,\"\"],0.11203796]}\n[true]\n[\"\\u001cDP\",[0.8626665,12114414041893822438]]\n{\"*\\u0016\\f1}\":{\"1\\u001b\":0.36654127,\"\\u0007;{\\\"\":{\"\":\"41e\\u0018\",\"\\u0015fT;\":11574806650096644037,\"Z\":\"_0\\u0006&X\"},\"vE+ \":2935046322892156719,\"\":\"\"}}\n{\"?+hf`\":{}}\n[null,[true,-9913222499256921221,null,15743710446996284792],{},true]\n[[[-9627130801059221053,null],4065208257197815031,null],-9767422520931038936]\n[[false,false,0.95248777],true,0.19347006]\n[true,null]\n[false,false,0.4905488,1.1336803e-3,\"J\"]\n[0.25115472,null,\"\",0.8550764]\n{\"b\\tH\":7491698839064921711,\"\":[0.9998612,false,null,0.665416,17844276928723417672],\"\\u0005t\":null}\n[{\"]\\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]]\n{\"=u_\":0.10595447,\"e86|?\":16584676174349563655,\"Y\\f\\ni\":true,\"\\t\":[]}\n{\"Y\\u000f\\u0007\":null}\n{\"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}\n[[{},{\"\\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}]\n[null]\n{\"9\\u000e\":\"v}NV\",\"~\\u0016t\":null}\n{\"\":5.3502917e-2,\"\":0.7454183,\"\\u001aq~\":[]}\n{\"]\":null,\"D\":\"l0L\",\"~*uN\":[],\"\":9954274599782580648}\n{\"\":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}\n[\"=\",0.11248773,4514021307219340329,true]\n[{\"\\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]\n[14162558928204775417,false,\"!\\u0016\\u0011\"]\n{\"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}\n[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,[]]\n[\"4T1\\r@\",0.46275347,false]\n{\"hly'l\":true}\n{\"\":[false],\"\":18311605067220884321,\"\\\\0\":{\"J#\\u0011\":\"\\b\",\"-\":0.48263913,\"\":[]}}\n[0.9280277,\"P\"]\n[true,{\"G\\\\vm\":true,\"7<{\\u0013m@\":\"\"}]\n{\"b\":-17176440223338641234,\"{~#1\":\"d\",\"T&\":0.3095814}\n[[\"5w\\u0012Y\\n\",-3465947354667783786,true],0.58169204]\n{\"\\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}}\n[\"\"]\n{\"z\\u0016\":0.65568596}\n[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\"]\n[null,{\" F7\\u0010\":12459903973742877697,\"\\u0005X\":0.36702603},\"\\u0017R%\\u0007\",-13502214511406886705,\"\\u001db\"]\n[\"%\\u001bj#\\u000e'\",0.29517365]\n{\"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}\n{\"T{|7#\":false,\"$>\":\"\\u001cB?8\"}\n{\"1}\\fEU\":{\"4\\u0017\\u001f\\u0007S\":209123727378233237}}\n[[[{\"?;\\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+\"]\n{\"5\\u0002\\f\":\"#&5t*\",\"b{6\\u001a\":-15275213607943662689,\"Y;\":17265934196169125293,\"\\nQlCs9\":null,\"\\u0018(:\":true}\n[{\"|\":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]\n{\"r;s\":-6593165038148587118}\n[-8006884604484209542]\n{\"F'R&wn\":0.82659614}\n{\"suU\\u001f\":0.8861345,\"A\\u0007rK\":[{},[-6776522806699827829,-10267898355349287368],\"\",false],\"\\u001fu4NH\":-2216197903302261055}\n{\"\\u001bZ`\":0.66861683,\"\":0.48398995,\"V\":0.30915624,\"+\":-1026948343628585707,\"y:\\u0005X\":0.2573502}\n{\"{~\":\"\",\"JV1\\t(B\":\",N\\u0018|_\"}\n[true,[],4.1036606e-2]\n[0.73778456,{\"/_E\":[false],\"s\":0.81533575},\"!?9dP\",true,false]\n{\"\":[\"i\"],\"\\u0018Lof\":{},\"\\u0014)d33)\":[1705660862319921361,[0.70245653,[{},false,-4997087011668413281,0.70262194]]],\"T\":0.22172987,\"\\u001e\":{\"q\":null,\"\\ncW\\u0016&f\":0.66210586}}\n{\"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}}\n[[\"+\\u000eMq\\u0015n\",\"0-Z\\u0011\"],{},0.19048035,{}]\n{\"\":0.17463982,\"B}O\\u0006h\":\"j\\u0016\\u0016;$\",\"v\":false,\"{q\\\\\":0.1645158}\n[[{},-10335304945929474818],\"\",[]]\n[{\"\":11011572605828908966,\"D\\\\y\":8.9524806e-2},0.12063557,null]\n[[{\"[ F\":[],\"\\fZ\\u001a+#\\u0012\":0.20774364,\"@u\":14140938735167775918,\"x\\\\\":[-11016694664242453733,true,0.7662049,null],\"f&\\u0012\":10041475338386091190}],[\"\"]]\n[{\"p\\u0006b\\u001cK\":null},{\"\":[[\".\\u0016\\u000ex`j\",-6879987747836866833,10223230372432915459,{\"4O\":1112402400137721488,\"iCH\\r\":-11294462142224825198,\" k^\":0.20589554},[8573083530212052064,\"W37/d\\t\",\"\"]],0.7530455]}]\n[0.66232306]\n{\"G\":null,\"C\":{\")UT}N\":0.5103738,\"Jv\\u001dy\\u000bQ\":\"5H~3\",\"\\u001d~\\u0016\\u0015\":true,\"\\u001f\\u0001?(\\u0017s\":2473312665277409295,\"|#m\":false}}\n[[{\"_)\":{\"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]\n[{\"\":\"<\\u001b\\u001dNr\"},3.2110214e-3,false,null,0.945265]\n[{\"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]]\n{\"U\":null,\"='!n)]\":null,\"/B\":4091446843733087751,\"R(fu\":{\"xe>2\":[[\"(\",false,{\"\":0.42793614}]]},\"-1\\u0015z\\u0005\\u0011\":false}\n[\"y\\u0007&I$\",false]\n[-13516489482431253430,\"cVx\"]\n[{\"\\u0007\\u0006g\\u001c\":-8554119748749916417},\"\\u001ch$SJ\\u0014\",{\"Fh\\u00050L7\":[]},{\"B\":\"\\u0010T\",\"\\u0015>[c u\":{\"\":\"M3X\"},\"\\u000f\\u0004tFu~\":0.36826915},-4617804547813974534]\n{\"\\u00135_\\u0014|y\":13469420637207143766,\"f01\":0.38382834,\"Zb\":{\"2z\":true,\"-oWAq\":-17693981350083080169,\"\\u0015\":-16142200838510853908},\"\":[-11453326168044549327,true,\"W3Z+\"]}\n[-11153910131317761898,\"\\u001ch\",[16314294140409942272,{},0.93278205,11399627290119531131],[]]\n{\"\\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}}\n{\"\\u0010-~\":[]}\n{\"O\\u0010\\u001fW\":{\"\\t-cpRB\":-1207104230604966545,\"&#\":false,\"K<\":{},\",e\":-3243935909847784433,\"=\\t\\u00039$N\":false},\"K\\\"ZA\":[false]}\n[8330608987917035325,18104787239307741033]\n{\"8!F~}L\":\"kvA|w\\u0017\",\"{\":{}}\n{\"]j\\u0013]\":13135044173561850680}\n{\"[\":-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]}\n[\"$\\u0005\",0.3907426]\n[8679419118201241337,0.19923568,0.668362,[null,0.44885856,-6773739461166169741,true],[]]\n[0.4405555,{\"\":true,\"\\u0002&6O\":-5924290840962216166,\"\\r\\u0017\\u0014C\":4.2134523e-3}]\n[0.9339055,\"\"]\n{\"\\u000f_\":{\"|^&m\":[],\"e\\u0004z\":{\"\\u0006+\":true,\"fb\":\"<W\",\"<\\u00184\\u0019\":\"v\\u001aZ\",\"\\bP\\u0003\\u001e\":null,\"\\u0019\":\"l\"},\"\\u001f\":\"Nr<yY\"},\"\":\"H\\u001c\"}\n{\"\":\"DO\"}\n[17256012902499945094]\n{\"\\tR7\":{\"l\\u000e\":[],\"\\t\\u0013Rx\\u0011h\":null,\"wP\":[14547570528401522133,14415130777834825591,{\"\":[3.6435902e-2]},\"S\\\"g\"],\"\\r\\u001e\\u0017\":\"1Tb#\\u0016\",\"\\u001c1[\\\"\":16344739751689581814}}\n{\" \\u0002\\bi\":false,\"Jh\":[[0.33667308,-17369336385787834625,\"\\u0003ar\",\"(\\u0012!\",[[true]]],-1396302974865016530,6.0578644e-2],\"\":\"d\\u0003j\\u000f\"}\n{\"\":-8991839176212064162,\"\\u000f\":0.87215024,\"99q\\u001b\\u0013,\":10827064753934197247,\"$v7;\":null,\"\\u0017pb\":false}\n{\"-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}\n{\"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}}\n[{\"~j\":\"\\\"0\",\"=r\\u0003\":\"*+r@0s\",\"\":0.19081467},\".h\\u0019}R\"]\n{\"\":\"ju\",\"m^~\":null}\n[[0.3681597],\"\",\"9\\u001ad\",\"e\\u000fw\"]\n{\"\\u0015~e\\u001bi%\":\"Kk\\u0018\"}\n{\"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 \"}\n{\"T/({J\":\"GAGU,\",\"T:\":9.503329e-2}\n[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]\n[-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]}]\n{\"\":\"h@?4~_\"}\n{\"\\u001cO2\":null,\"ug`N\":0.25825077}\n{\":zO\":-17886397144894567202}\n[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]\n[{\"\\u0018\":-9879941084256315652,\"\":0.5923274,\"\\\\%\\b:\":16734910719493405340,\"Sff\":\" F;\"}]\n{\"\\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\":{}}}\n[\"\\u0003\"]\n{\"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}]}}\n{\"0\":{\"^\":false,\"\\u001d[Y\":true,\"\\u00016\\u0011%\":{\"^\":-13422956777875372902,\"\":0.6748177,\"UK\":-14185007865157151724,\"#\":[]},\"lV\\f\":16496414788763842682,\"\\\\1hs\":0.75634325},\"\":\"J$cjy\"}\n{\"j\\u0018L\\u0010\":null,\"~C\":0.5380389,\"wWfI\\u001d\":7214825108176793749,\"\\u0002*k\":false}\n[[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}}]}]]]\n{\"F\":[0.28045475,6395486746026534910],\"\":[],\"=\\n&@?%\":0.75717854}\n[[\"\"],[-4429524012470214935,-2796661001313312842]]\n[false]\n[\"O\\u0011ak!f\",0.31498963,false,[true]]\n{\"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\":\"baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar\"}\n{\"vh#wH\":null,\"*l\":2296691638,\"W\\u0002\":3606753554,\"`\\u000e.\\b\":0.385041,\"N3h4m\":null}\n{\"M\\u00185-\":[0.86986375,0.33169776,\"\\u0015\",true,-3787920576],\"\":-2266678859,\"\":{\"\\u001d\":3294973006,\"x(\\u001d|\":0.41438067,\"#q\":true,\"\":null},\"L$\":[false],\"op[bi\":null}\n[\"'{\\\\y[\",\"qK\",2505258107]\n{\"\\u0014\\u0016\":true,\"7\\u0013\":true,\"\":[null]}\n[null,[0.75785536],\"\"]\n{\":\":0.35312474,\"*\\nk\":\"?\\u0014N)\"}\n{\"\":0.50028557,\"P\\u0005)&\\u0002\":null,\"=\\u0002y0\":-2062734622}\n[0.30801737,-2716449190,\"6$am\\u0002m\",1.2181699e-2]\n[{\"\\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}\":{}}}]\n[0.9796113]\n[0.6591127,[\"\"],[0.79424393],{\"XE\\u001ep\":{\".(\\u0016\":false},\"Z?lR\\u0015,\":\"\",\"\\nz\":[false,3117789160,false],\"sd#P\\u0004\":2807155556,\"\\u0010\":null},-1784589105]\n{\"+YJ(\":null,\"Aq#^_$\":-2277615052,\"\":0.97045594}\n[\"[\\u001e*Pdj\",null]\n{\"<\\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}\n[\"\\th/y\\\"\",0.6817276]\n{\")k\":[0.38475662,45616771,{\"\\u0002p\\u0002{E\":true,\"O\":596477863,\"KoSE/C\":null},0.9854614],\"\":-2886538406,\"D+[R\":\"Yw\\u000b{\\u0015\"}\n[0.5344141,\"P\\u0017sJ\\u00072\",0.71697176,3692993782]\n[{\"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}]\n{\"a\\u0013\\fE\":false,\"\\u000e+*d\\u000e\":605395782,\"/\\u0004q\\u0014\\\"\":\"Q:\\u0016\\u001f\",\"x$fJ\":{}}\n[[\"9\"],true,0.43510032,{\"`AzY\":{\"\":false,\"zv\":null,\"mRI\\u0011\":[true,[[3883676960]]],\"DU\\u0002\":\"SCC~T\"}}]\n[[-3290883344,0.54075867,0.7522493,null,null],\"\\r\\u000f\\u0015[\",2429340856]\n[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\n{\"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}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}\n"
  },
  {
    "path": "tests/test-quote.c",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#include \"json65-quote.h\"\n\nstatic void do_test (const char *s) {\n    fputc ('\\\"', stdout);\n    j65_print_escaped (s, stdout);\n    fputs (\"\\\"\\n\", stdout);\n}\n\nint main (int argc, char **argv) {\n    do_test (\"Hello, World!\");\n    do_test (\"Hello, World!\\n\");\n    do_test (\"Hello,\\r\\nWorld!\");\n    do_test (\"Hello, \\\"World!\\\"\");\n    do_test (\"\\aHello, World!\");\n    do_test (\"Backslash \\\\\");\n    do_test (\"Hello,\\tWorld!\");\n    do_test (\"\\001\\002\\003\");\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/test-string.c",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#include <stdio.h>\n#include <string.h>\n#include \"json65-string.h\"\n\n#define ITERATIONS 300\n\nstatic j65_strings strs;\nstatic const char *results[ITERATIONS];\nstatic char buf1[10];\nstatic char buf2[10];\n\nstatic void print_bucket_usage (void) {\n    uint8_t *lo;\n    uint8_t *hi;\n    uint16_t i, used = 0;\n\n    lo = (uint8_t *) &strs;\n    hi = lo + 256;\n\n    for (i = 0 ; i < 256 ; i++) {\n        if (lo[i] || hi[i])\n            used++;\n    }\n\n    printf (\"used %u/256 buckets\\n\", used);\n}\n\nint main (int argc, char **argv) {\n    uint16_t i;\n    const char *tmp;\n\n    j65_init_strings (&strs);\n\n    for (i = 0 ; i < ITERATIONS ; i++) {\n        snprintf (buf1, sizeof (buf1), \"%u\", i);\n        results[i] = j65_intern_string (&strs, buf1);\n        if (strcmp (buf1, results[i]) != 0) {\n            printf (\"first loop: '%.20s' (%p) not equal to '%s' (%p)\\n\",\n                    results[i], results[i],\n                    buf1, buf1);\n            return 1;\n        }\n    }\n\n    for (i = 0 ; i < ITERATIONS ; i++) {\n        snprintf (buf2, sizeof (buf2), \"%u\", i);\n        tmp = j65_intern_string (&strs, buf2);\n        if (strcmp (buf2, tmp) != 0) {\n            printf (\"second loop: '%s' not equal to '%s'\\n\", tmp, buf2);\n            return 1;\n        }\n        if (tmp != results[i]) {\n            printf (\"For '%s', %p not equal to %p\\n\", buf2, tmp, results[i]);\n            return 1;\n        }\n    }\n\n    print_bucket_usage ();\n    j65_free_strings (&strs);\n\n    printf (\"Success!\\n\");\n    return 0;\n}\n"
  },
  {
    "path": "tests/test-tree.c",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#include <stdio.h>\n#include <string.h>\n#include \"json65-tree.h\"\n\nstatic char buf[1024];\nstatic j65_parser parser;\nstatic j65_tree tree;\nstatic const char infile[] = \"test-tree.json\";\n\nstatic int do_test (size_t len) {\n    int8_t status;\n    j65_node *n;\n    uint8_t node_type;\n    const char *str;\n    uint32_t line_number, column_number;\n\n    j65_init_tree (&tree);\n    j65_init (&parser, &tree, j65_tree_callback, 255);\n    status = j65_parse (&parser, buf, len);\n    if (status != J65_DONE) {\n        fprintf (stderr, \"j65_parse returned status %d\\n\", status);\n        return 1;\n    }\n\n    n = j65_find_key (&tree, tree.root, \"color\");\n    if (n == NULL) {\n        fprintf (stderr, \"couldn't find color\\n\");\n        return 1;\n    }\n\n    n = n->child;\n    n = j65_find_key (&tree, n, \"linearCutoff\");\n    if (n == NULL) {\n        fprintf (stderr, \"couldn't find linearCutoff\\n\");\n        return 1;\n    }\n\n    n = n->child;\n    node_type = n->node_type;\n    if (node_type != J65_NUMBER) {\n        fprintf (stderr, \"%u != J65_NUMBER\\n\", node_type);\n        return 1;\n    }\n\n    str = n->string;\n    if (0 != strcmp (str, \"0.0078125\")) {\n        fprintf (stderr, \"%s != 0.0078125\\n\", str);\n        return 1;\n    }\n\n    line_number = n->location.line_number + 1;\n    column_number = n->location.column_number;\n    if (line_number != 8) {\n        fprintf (stderr, \"line number %lu != 8\\n\", line_number);\n        return 1;\n    }\n\n    if (column_number != 33) {\n        fprintf (stderr, \"column number %lu != 33\\n\", column_number);\n        return 1;\n    }\n\n    if (n->location.line_offset != 135) {\n        fprintf (stderr, \"line offset %lu != 135\\n\", n->location.line_offset);\n        return 1;\n    }\n\n    n = j65_find_key (&tree, tree.root, \"banana\");\n    if (n != NULL) {\n        fprintf (stderr, \"found banana but shouldn't have\\n\");\n        return 1;\n    }\n\n    j65_free_tree (&tree);\n    return 0;\n}\n\nint main (int argc, char **argv) {\n    FILE *f;\n    int badness = 0;\n    size_t len;\n\n    f = fopen (infile, \"r\");\n    if (! f) {\n        fprintf (stderr, \"Couldn't open file '%s' for reading\\n\", infile);\n        return 1;\n    }\n\n    len = fread (buf, 1, sizeof (buf), f);\n    if (ferror (f)) {\n        fprintf (stderr, \"Couldn't read file '%s'\\n\", infile);\n        return 1;\n    }\n\n    fclose (f);\n\n    badness = do_test (len);\n\n    if (badness == 0)\n        fprintf (stderr, \"Success!\\n\");\n\n    return badness;\n}\n"
  },
  {
    "path": "tests/test-tree.json",
    "content": "{\n    \"listen\": [\"127.0.0.1\", 7890],\n    \"verbose\": true,\n\n    \"color\": {\n        \"gamma\": 2.5,\n        \"whitepoint\": [1.0, 1.0, 1.0],\n        \"linearCutoff\": 0.0078125\n    },\n\n    \"devices\": [\n        {\n            \"type\": \"fadecandy\",\n            \"map\": [\n                [ 0,  0,  0, 50, \"grb\" ],\n                [ 0, 50, 64, 50, \"grb\" ]\n            ]\n        }\n    ]\n}\n"
  },
  {
    "path": "tests/test.c",
    "content": "/*\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n*/\n\n#include <stdio.h>\n#include <string.h>\n#include <json65.h>\n\nstatic j65_parser parser;\nstatic int passes, failures;\nstatic char buf[80];\n\n#define MAGIC 0x2badbeef\n\ntypedef struct {\n    int8_t ev;\n    int32_t integer;\n    const char *str;\n    uint32_t line_no;\n    uint8_t depth;\n} event_check;\n\ntypedef struct {\n    uint32_t magic;\n    const event_check *events;\n    size_t len;\n    size_t pos;\n} my_context;\n\nstatic const event_check test00[] = {\n  { J65_DONE, 0, \"[] \", 0, 0 },\n  { J65_START_ARRAY,       0, NULL,             0, 1 },\n  { J65_END_ARRAY,         0, NULL,             0, 1 },\n};\n\nstatic const event_check test01[] = {\n  { J65_DONE, 1, \"{} \", 0, 0 },\n  { J65_START_OBJ,         0, NULL,             0, 1 },\n  { J65_END_OBJ,           0, NULL,             0, 1 },\n};\n\nstatic const event_check test02[] = {\n  { J65_DONE, 2, \"1234 \", 0, 0 },\n  { J65_INTEGER,        1234, \"1234\",           0, 0 },\n};\n\nstatic const event_check test03[] = {\n  { J65_DONE, 3, \"-10000000 \", 0, 0 },\n  { J65_INTEGER,   -10000000, \"-10000000\",      0, 0 },\n};\n\nstatic const event_check test04[] = {\n  { J65_DONE, 4, \"1.5 \", 0, 0 },\n  { J65_NUMBER,            0, \"1.5\",            0, 0 },\n};\n\nstatic const event_check test05[] = {\n  { J65_DONE, 5, \"1e-2 \", 0, 0 },\n  { J65_NUMBER,            0, \"1e-2\",           0, 0 },\n};\n\nstatic const event_check test06[] = {\n  { J65_DONE, 6, \"\\\"Hello, World\\\" \", 0, 0 },\n  { J65_STRING,            0, \"Hello, World\",   0, 0 },\n};\n\nstatic const event_check test07[] = {\n  { J65_DONE, 7, \"null \", 0, 0 },\n  { J65_NULL,              0, NULL,             0, 0 },\n};\n\nstatic const event_check test08[] = {\n  { J65_DONE, 8, \"false \", 0, 0 },\n  { J65_FALSE,             0, NULL,             0, 0 },\n};\n\nstatic const event_check test09[] = {\n  { J65_DONE, 9, \"true \", 0, 0 },\n  { J65_TRUE,              0, NULL,             0, 0 },\n};\n\nstatic const event_check test10[] = {\n  { J65_DONE, 10, \"{\\\"foo\\\": 5} \", 0, 0 },\n  { J65_START_OBJ,         0, NULL,             0, 1 },\n  { J65_KEY,               0, \"foo\",            0, 1 },\n  { J65_INTEGER,           5, \"5\",              0, 1 },\n  { J65_END_OBJ,           0, NULL,             0, 1 },\n};\n\nstatic const event_check test11[] = {\n  { J65_DONE, 11, \"{\\\"foo\\\": \\\"bar\\\", \\\"baz\\\": [1, 2, 3]} \", 0, 0 },\n  { J65_START_OBJ,         0, NULL,             0, 1 },\n  { J65_KEY,               0, \"foo\",            0, 1 },\n  { J65_STRING,            0, \"bar\",            0, 1 },\n  { J65_KEY,               0, \"baz\",            0, 1 },\n  { J65_START_ARRAY,       0, NULL,             0, 2 },\n  { J65_INTEGER,           1, \"1\",              0, 2 },\n  { J65_INTEGER,           2, \"2\",              0, 2 },\n  { J65_INTEGER,           3, \"3\",              0, 2 },\n  { J65_END_ARRAY,         0, NULL,             0, 2 },\n  { J65_END_OBJ,           0, NULL,             0, 1 },\n};\n\nstatic const event_check test12[] = {\n    { J65_WANT_MORE, 12, \"[1, 2, 3\", 0, 0 },\n    { J65_START_ARRAY,     0, NULL,             0, 1 },\n    { J65_INTEGER,         1, \"1\",              0, 1 },\n    { J65_INTEGER,         2, \"2\",              0, 1 },\n};\n\nstatic const event_check test13[] = {\n    { J65_DONE, 13, \"\\n\\\"slash \\\\/ tab \\\\t\\\"\", 1, 0 },\n    { J65_STRING,          0, \"slash / tab \\t\", 1, 0 },\n};\n\nstatic const event_check test14[] = {\n    { J65_DONE, 14, \"\\\"slash \\\\/ backslash \\\\\\\\ tab \\\\t\\\"\", 0, 0 },\n    { J65_STRING,          0, \"slash / backslash \\\\ tab \\t\", 0, 0 },\n};\n\nstatic const event_check test15[] = {\n    { J65_DONE, 15, \"\\\"have \\\\u0061 nice day\\\"\", 0, 0 },\n    { J65_STRING,          0, \"have a nice day\", 0, 0 },\n};\n\nstatic const event_check test16[] = {\n    { J65_DONE, 16, \"\\\"have \\\\u0061\\\\u0020nice day\\\"\", 0, 0 },\n    { J65_STRING,          0, \"have a nice day\", 0, 0 },\n};\n\nstatic const event_check test17[] = {\n    { J65_DONE, 17, \"\\\"have \\\\u0061\\\\\\\\nice day\\\"\", 0, 0 },\n    { J65_STRING,          0, \"have a\\\\nice day\", 0, 0 },\n};\n\nstatic const event_check test18[] = {\n    { J65_DONE, 18, \"\\\"this \\\\uD834\\\\uDD1E is a G clef\\\"\", 0, 0 },\n    { J65_STRING,          0, \"this 𝄞 is a G clef\", 0, 0 },\n};\n\nstatic const event_check test19[] = {\n    { J65_DONE, 19, \"\\\"\\\\u00a9 2018\\\"\", 0, 0 },\n    { J65_STRING,          0, \"© 2018\", 0, 0 },\n};\n\nstatic const event_check test20[] = {\n    { J65_DONE, 20, \"\\\"cents \\\\u00a2 Euros \\\\u20ac\\\"\", 0, 0 },\n    { J65_STRING,          0, \"cents ¢ Euros €\", 0, 0 },\n};\n\nstatic const event_check test21[] = {\n    { J65_DONE, 21, \"[\\nnull\\n,\\nfalse\\n,\\ntrue\\n]\\n\", 6, 0 },\n    { J65_START_ARRAY,     0, NULL,              0, 1 },\n    { J65_NULL,            0, NULL,              1, 1 },\n    { J65_FALSE,           0, NULL,              3, 1 },\n    { J65_TRUE,            0, NULL,              5, 1 },\n    { J65_END_ARRAY,       0, NULL,              6, 1 },\n};\n\nstatic const event_check test22[] = {\n    { J65_EXPECTED_STRING, 22, \"{false: true} \", 0, 0 },\n    { J65_START_OBJ,       0, NULL,              0, 1 },\n};\n\nstatic const event_check test23[] = {\n    { J65_EXPECTED_COLON, 23, \"{\\\"hello\\\", \\\"world\\\"} \", 0, 0 },\n    { J65_START_OBJ,       0, NULL,              0, 1 },\n    { J65_KEY,             0, \"hello\",           0, 1 },\n};\n\nstatic const event_check test24[] = {\n    { J65_DONE, 24, \"[\\rnull\\r,\\rfalse\\r,\\rtrue\\r]\\r\", 6, 0 },\n    { J65_START_ARRAY,     0, NULL,              0, 1 },\n    { J65_NULL,            0, NULL,              1, 1 },\n    { J65_FALSE,           0, NULL,              3, 1 },\n    { J65_TRUE,            0, NULL,              5, 1 },\n    { J65_END_ARRAY,       0, NULL,              6, 1 },\n};\n\nstatic const event_check test25[] = {\n    { J65_DONE, 25, \"[\\r\\nnull\\r\\n,\\r\\nfalse\\r\\n,\\r\\ntrue\\r\\n]\\r\\n\", 6, 0 },\n    { J65_START_ARRAY,     0, NULL,              0, 1 },\n    { J65_NULL,            0, NULL,              1, 1 },\n    { J65_FALSE,           0, NULL,              3, 1 },\n    { J65_TRUE,            0, NULL,              5, 1 },\n    { J65_END_ARRAY,       0, NULL,              6, 1 },\n};\n\nstatic const event_check test26[] = {\n  { J65_DONE, 26, \"2147483647 \", 0, 0 },\n  { J65_INTEGER,  2147483647, \"2147483647\",           0, 0 },\n};\n\nstatic const event_check test27[] = {\n  { J65_DONE, 27, \"-2147483647 \", 0, 0 },\n  { J65_INTEGER, -2147483647, \"-2147483647\",          0, 0 },\n};\n\nstatic const event_check test28[] = {\n  { J65_DONE, 28, \"2147483648 \", 0, 0 },\n  { J65_NUMBER,            0, \"2147483648\",           0, 0 },\n};\n\nstatic const event_check test29[] = {\n  { J65_DONE, 29, \"-2147483648 \", 0, 0 },\n  { J65_INTEGER, -2147483648, \"-2147483648\",          0, 0 },\n};\n\nstatic const event_check test30[] = {\n  { J65_DONE, 30, \"-2147483649 \", 0, 0 },\n  { J65_NUMBER,            0, \"-2147483649\",          0, 0 },\n};\n\nstatic const event_check test31[] = {\n  { J65_DONE, 31, \"-4294967296 \", 0, 0 },\n  { J65_NUMBER,            0, \"-4294967296\",          0, 0 },\n};\n\nstatic const event_check test32[] = {\n  { J65_DONE, 32, \"4294967296 \", 0, 0 },\n  { J65_NUMBER,            0, \"4294967296\",           0, 0 },\n};\n\nstatic const event_check test33[] = {\n  { J65_DONE, 33, \"10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \", 0, 0 },\n  { J65_NUMBER, 0, \"10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\", 0, 0 },\n};\n\nstatic const event_check test34[] = {\n  { J65_DONE, 34, \"-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \", 0, 0 },\n  { J65_NUMBER, 0, \"-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\", 0, 0 },\n};\n\nstatic const event_check test35[] = {\n  { J65_STRING_TOO_LONG, 35, \"10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \", 0, 0 },\n};\n\nstatic const event_check test36[] = {\n  { J65_PARSE_ERROR, 36, \"5-5 \", 0, 0 },\n};\n\nstatic const event_check test37[] = {\n  { J65_ILLEGAL_CHAR, 37, \"0x2000 \", 0, 0 },\n  { J65_INTEGER,       0, \"0\",       0, 0 },\n};\n\nstatic const event_check test38[] = {\n  { J65_ILLEGAL_CHAR, 38, \"barf \", 0, 0 },\n};\n\nstatic const event_check test39[] = {\n  { J65_PARSE_ERROR,  39, \"nue \", 0, 0 },\n};\n\nstatic const event_check test40[] = {\n    { J65_PARSE_ERROR, 40, \"]\", 0, 0 },\n};\n\nstatic const event_check test41[] = {\n    { J65_ILLEGAL_ESCAPE, 41, \"\\\"This is \\\\j not allowed\\\"\", 0, 0 },\n};\n\nstatic const event_check test42[] = {\n    { J65_ILLEGAL_ESCAPE, 42, \"\\\"This is \\\\uucp not either\\\"\", 0, 0 },\n};\n\nstatic const event_check test43[] = {\n    { J65_ILLEGAL_ESCAPE, 43, \"\\\"And this? \\\\u\\\"\", 0, 0 },\n};\n\nstatic const event_check test44[] = {\n    { J65_DONE, 44, \"{\\\"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\\\": \\\"baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar\\\"}\", 0, 0 },\n    { J65_START_OBJ,         0, NULL,             0, 1 },\n    { J65_KEY,               0, \"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\", 0, 1 },\n    { J65_STRING,            0, \"baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar\", 0, 1 },\n    { J65_END_OBJ,           0, NULL,             0, 1 },\n};\n\nstatic const event_check test45[] = {\n    { J65_DONE, 45, \"[[[[]]]]\", 0, 0 },\n    { J65_START_ARRAY,       0, NULL,             0, 1 },\n    { J65_START_ARRAY,       0, NULL,             0, 2 },\n    { J65_START_ARRAY,       0, NULL,             0, 3 },\n    { J65_START_ARRAY,       0, NULL,             0, 4 },\n    { J65_END_ARRAY,         0, NULL,             0, 4 },\n    { J65_END_ARRAY,         0, NULL,             0, 3 },\n    { J65_END_ARRAY,         0, NULL,             0, 2 },\n    { J65_END_ARRAY,         0, NULL,             0, 1 },\n};\n\nstatic const event_check test46[] = {\n    { J65_NESTING_TOO_DEEP, 46, \"[[[[[]]]]]\", 0, 0 },\n    { J65_START_ARRAY,       0, NULL,             0, 1 },\n    { J65_START_ARRAY,       0, NULL,             0, 2 },\n    { J65_START_ARRAY,       0, NULL,             0, 3 },\n    { J65_START_ARRAY,       0, NULL,             0, 4 },\n};\n\nstatic const event_check test47[] = {\n    { J65_PARSE_ERROR, 47, \"[\\n    \\\"an\\\",\\n    \\\"extra\\\",\\n    \\\"comma\\\",\\n]\", 4, 0 },\n    { J65_START_ARRAY,       0, NULL,             0, 1 },\n    { J65_STRING,            0, \"an\",             1, 1 },\n    { J65_STRING,            0, \"extra\",          2, 1 },\n    { J65_STRING,            0, \"comma\",          3, 1 },\n};\n\nstatic const event_check test48[] = {\n    { J65_EXPECTED_STRING,  48, \"{ \\\"extra\\\": \\\"comma\\\", }\", 0, 0 },\n    { J65_START_OBJ,         0, NULL,             0, 1 },\n    { J65_KEY,               0, \"extra\",          0, 1 },\n    { J65_STRING,            0, \"comma\",          0, 1 },\n};\n\nstatic const char *event_name (uint8_t event) {\n    switch (event) {\n    case J65_NULL        : return \"J65_NULL\";\n    case J65_FALSE       : return \"J65_FALSE\";\n    case J65_TRUE        : return \"J65_TRUE\";\n    case J65_INTEGER     : return \"J65_INTEGER\";\n    case J65_NUMBER      : return \"J65_NUMBER\";\n    case J65_STRING      : return \"J65_STRING\";\n    case J65_KEY         : return \"J65_KEY\";\n    case J65_START_OBJ   : return \"J65_START_OBJ\";\n    case J65_END_OBJ     : return \"J65_END_OBJ\";\n    case J65_START_ARRAY : return \"J65_START_ARRAY\";\n    case J65_END_ARRAY   : return \"J65_END_ARRAY\";\n    default: return \"?\";\n    }\n}\n\nstatic void print_pass (void) {\n    printf (\"\\033[32mPASS\\033[0m\\n\");\n    passes++;\n}\n\nstatic void print_fail (void) {\n    printf (\"\\033[31mFAIL\\033[0m\\n\");\n    failures++;\n}\n\nstatic int8_t callback (j65_parser *p, uint8_t event) {\n    my_context *ctx = (my_context *) j65_get_context(p);\n    const char *ename = event_name (event);\n    size_t pos = ctx->pos;\n    const event_check *ec;\n    int32_t i;\n    const char *str;\n    size_t len1, len2;\n    uint32_t line_no;\n    uint8_t depth;\n\n    if (ctx->magic != MAGIC) {\n        print_fail();\n        printf (\"Got magic $%08lx but expected $%08lx\\n\", ctx->magic, MAGIC);\n        return J65_USER_ERROR;\n    }\n\n    if (pos >= ctx->len) {\n        print_fail();\n        printf (\"Got extra event of type %s\\n\", ename);\n        return J65_USER_ERROR;\n    }\n\n    ec = ctx->events + pos;\n\n    if (ec->ev != event) {\n        print_fail();\n        printf (\"[%u] Got %s but expected %s\\n\",\n                pos, ename, event_name(ec->ev));\n        return J65_USER_ERROR;\n    }\n\n    if (event == J65_INTEGER) {\n        i = j65_get_integer(p);\n        if (i != ec->integer) {\n            print_fail();\n            printf (\"[%u] Got %ld but expected %ld\\n\", pos, i, ec->integer);\n            return J65_USER_ERROR;\n        }\n    }\n\n    if (event == J65_INTEGER || event == J65_NUMBER ||\n        event == J65_STRING  || event == J65_KEY) {\n        str = j65_get_string(p);\n        len1 = j65_get_length(p);\n        len2 = strlen (str);\n\n        if (len1 != len2) {\n            print_fail();\n            printf (\"[%u] String length is %u but claimed to be %u\\n\",\n                    pos, len2, len1);\n            return J65_USER_ERROR;\n        }\n\n        if (strcmp (str, ec->str) != 0) {\n            print_fail();\n            printf (\"[%u] For %s, got '%s' but expected '%s'\\n\",\n                    pos, ename, str, ec->str);\n            return J65_USER_ERROR;\n        }\n    }\n\n    line_no = j65_get_line_number(p);\n    if (line_no != ec->line_no) {\n        print_fail();\n        printf (\"[%u] For %s, got line %lu but expected %lu\\n\",\n                pos, ename, line_no, ec->line_no);\n        return J65_USER_ERROR;\n    }\n\n    depth = j65_get_current_depth (p);\n    if (depth != ec->depth) {\n        print_fail ();\n        printf (\"[%u] For %s, got depth %u but expected %u\\n\",\n                pos, ename, depth, ec->depth);\n        return J65_USER_ERROR;\n    }\n\n    ctx->pos++;\n\n    return 0;\n}\n\nstatic void run_test (const event_check *events, size_t len) {\n    my_context ctx;\n    uint32_t line_no;\n    int8_t ret;\n    const char *str = events->str;\n\n    printf (\"test %02ld: \", events->integer);\n\n    ctx.magic = MAGIC;\n    ctx.events = events;\n    ctx.len = len;\n    ctx.pos = 1;\n\n    /* Use a small nesting depth to make it easy to test. */\n    j65_init (&parser, (void *) &ctx, callback, 4);\n    ret = j65_parse(&parser, str, strlen(str));\n\n    if (ret == J65_USER_ERROR) {\n        return;\n    }\n\n    if (ret != events->ev) {\n        print_fail();\n        printf (\"Got return code %d but expected %d\\n\", ret, events->ev);\n        return;\n    }\n\n    if (ctx.pos != ctx.len) {\n        print_fail();\n        printf (\"Got %u events but expected %u\\n\", ctx.pos - 1, ctx.len - 1);\n        return;\n    }\n\n    line_no = j65_get_line_number(&parser);\n    if (line_no != events->line_no) {\n        print_fail();\n        printf (\"Final line number was %lu but expected %lu\\n\",\n                line_no, events->line_no);\n        return;\n    }\n\n    print_pass();\n}\n\nstatic void depth_test (uint8_t specified, uint8_t expected) {\n    uint8_t actual;\n\n    snprintf (buf, sizeof (buf), \"depth test (%u):\", specified);\n    printf (\"%-18s\", buf);\n\n    j65_init (&parser, NULL, NULL, specified);\n    actual = j65_get_max_depth (&parser);\n\n    if (actual != expected) {\n        print_fail ();\n        printf (\"Got %u but expected %u\\n\", actual, expected);\n    } else {\n        print_pass ();\n    }\n}\n\n#define TEST(x) run_test (x, sizeof(x) / sizeof(x[0]))\n\nint main (int argc, char **argv) {\n    int color;\n\n    TEST(test00);\n    TEST(test01);\n    TEST(test02);\n    TEST(test03);\n    TEST(test04);\n    TEST(test05);\n    TEST(test06);\n    TEST(test07);\n    TEST(test08);\n    TEST(test09);\n    TEST(test10);\n    TEST(test11);\n    TEST(test12);\n    TEST(test13);\n    TEST(test14);\n    TEST(test15);\n    TEST(test16);\n    TEST(test17);\n    TEST(test18);\n    TEST(test19);\n    TEST(test20);\n    TEST(test21);\n    TEST(test22);\n    TEST(test23);\n    TEST(test24);\n    TEST(test25);\n    TEST(test26);\n    TEST(test27);\n    TEST(test28);\n    TEST(test29);\n    TEST(test30);\n    TEST(test31);\n    TEST(test32);\n    TEST(test33);\n    TEST(test34);\n    TEST(test35);\n    TEST(test36);\n    TEST(test37);\n    TEST(test38);\n    TEST(test39);\n    TEST(test40);\n    TEST(test41);\n    TEST(test42);\n    TEST(test43);\n    TEST(test44);\n    TEST(test45);\n    TEST(test46);\n    TEST(test47);\n    TEST(test48);\n\n    depth_test (0, 224);\n    depth_test (1, 1);\n    depth_test (16, 16);\n    depth_test (123, 123);\n    depth_test (224, 224);\n    depth_test (255, 224);\n\n    if (failures > 0)\n        color = 31;             /* red */\n    else\n        color = 32;             /* green */\n\n    printf (\"\\033[%dm================================\\033[0m\\n\", color);\n    printf (\"%d tests passed; %d tests failed\\n\", passes, failures);\n    printf (\"\\033[%dm================================\\033[0m\\n\", color);\n\n    return failures;\n}\n"
  },
  {
    "path": "tools/README.md",
    "content": "This directory contains some scripts I used when making JSON65.\nYou probably won't need them.\n"
  },
  {
    "path": "tools/asm-heading.hs",
    "content": "#!/usr/bin/env stack\n-- stack --resolver lts-12.6 --install-ghc runghc --package text\n\n{-# LANGUAGE OverloadedStrings #-}\n\n{-\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n-}\n\nimport Control.Monad\nimport qualified Data.Text as T\nimport qualified Data.Text.IO as T\nimport System.Environment\n\nwidth :: Int\nwidth = 70\n\nmain = do\n  args <- getArgs\n  forM_ args $ \\arg -> do\n    let title = T.pack arg\n        semis = T.replicate width \";\"\n    T.putStrLn semis\n    T.putStrLn $ \";;\" <> T.center (width - 4) ' ' title <> \";;\"\n    T.putStrLn semis\n    T.putStrLn \"\"\n"
  },
  {
    "path": "tools/check-endproc.pl",
    "content": "#!/usr/bin/perl -w\n\n# Script to check that the comment on the \".endproc\" line matches\n# the name on the corresponding \".proc\" line.\n\nuse strict;\n\nmy $name     = undef;\nmy $procLine = undef;\nmy $exitCode = 0;\n\nwhile (<>) {\n    chomp;\n\n    if (/^\\.proc\\s+(\\S+)/) {\n        $name     = $1;\n        $procLine = $.;\n    } elsif (/^.endproc/) {\n        if (/;\\s*(\\S+)/) {\n            my $endName = $1;\n\n            if ($name ne $endName) {\n                print STDERR\n                    \"$ARGV: $endName on line $. but \",\n                    \"$name on line $procLine\\n\";\n                $exitCode = 1;\n            }\n        } else {\n            print STDERR\n                \"$ARGV: no name on line $. but \",\n                \"$name on line $procLine\\n\";\n            $exitCode = 1;\n        }\n    }\n} continue {\n    close ARGV if eof;  # Not eof()!\n}\n\nexit ($exitCode);\n"
  },
  {
    "path": "tools/convert_enums.pl",
    "content": "#!/usr/bin/perl -w\n\n# JSON65 - A JSON parser for the 6502 microprocessor.\n#\n# https://github.com/ppelleti/json65\n#\n# Copyright © 2018 Patrick Pelletier\n#\n# This software is provided 'as-is', without any express or implied\n# warranty.  In no event will the authors be held liable for any damages\n# arising from the use of this software.\n#\n# Permission is granted to anyone to use this software for any purpose,\n# including commercial applications, and to alter it and redistribute it\n# freely, subject to the following restrictions:\n#\n# 1. The origin of this software must not be misrepresented; you must not\n#    claim that you wrote the original software. If you use this software\n#    in a product, an acknowledgment in the product documentation would be\n#    appreciated but is not required.\n# 2. Altered source versions must be plainly marked as such, and must not be\n#    misrepresented as being the original software.\n# 3. This notice may not be removed or altered from any source distribution.\n\nmy $inenum = 0;\n\nsub hexify {\n    my $arg = $_[0];\n    $arg =~ s/0x/\\$/;\n    $arg =~ s/(-\\d+)/sprintf \"\\$%02x\", $1 & 0xff/e;\n    return $arg;\n}\n\nwhile (<>) {\n    chomp;\n    if (/^enum\\s+(\\w+)/) {\n        print \";; $1\\n\";\n        print \".enum\\n\";\n        $inenum = 1;\n    } elsif (/^\\}\\;/) {\n        print \".endenum\\n\\n\";\n        $inenum = 0;\n    } elsif ($inenum) {\n        s/^\\s+(\\w+\\s*)(=?\\s*[-\\w]*),/\"$1\".hexify($2)/e;\n        s%/\\*(.*)\\*/%;$1%;\n        s/^\\s*/        /;\n        s/\\s+$//;\n        print $_, \"\\n\";\n    }\n}\n"
  },
  {
    "path": "tools/debug.inc",
    "content": ";; JSON65 - A JSON parser for the 6502 microprocessor.\n;;\n;; https://github.com/ppelleti/json65\n;;\n;; Copyright © 2018 Patrick Pelletier\n;;\n;; This software is provided 'as-is', without any express or implied\n;; warranty.  In no event will the authors be held liable for any damages\n;; arising from the use of this software.\n;;\n;; Permission is granted to anyone to use this software for any purpose,\n;; including commercial applications, and to alter it and redistribute it\n;; freely, subject to the following restrictions:\n;;\n;; 1. The origin of this software must not be misrepresented; you must not\n;;    claim that you wrote the original software. If you use this software\n;;    in a product, an acknowledgment in the product documentation would be\n;;    appreciated but is not required.\n;; 2. Altered source versions must be plainly marked as such, and must not be\n;;    misrepresented as being the original software.\n;; 3. This notice may not be removed or altered from any source distribution.\n\n        .import pusha0\n        .import pushax\n        .import _printf\n\n        zp_bytes = 24\n\n        .data\nsave0a: .byte 0\nsave0x: .byte 0\nsave_a: .byte 0\nsave_x: .byte 0\nsave_y: .byte 0\nsave_zp:\n        .res zp_bytes\n        .code\n\n.macro save_regs\n        .local L\n        php\n        sta save_a\n        stx save_x\n        sty save_y\n        ldy #zp_bytes-1\nL:      lda sreg,y\n        sta save_zp,y\n        dey\n        bpl L\n.endmacro               ; save_regs\n\n.macro restore_regs\n        .local L\n        ldy #zp_bytes-1\nL:      lda save_zp,y\n        sta sreg,y\n        dey\n        bpl L\n        ldy save_y\n        ldx save_x\n        lda save_a\n        plp\n.endmacro               ; restore_regs\n\n.macro print_str str\n        .local S\n        .data\nS:      .asciiz str\n        .code\n        php\n        sta save0a\n        stx save0x\n        lda #<S\n        ldx #>S\n        jsr debug_str\n        ldx save0x\n        lda save0a\n        plp\n.endmacro               ; print_str\n\n.macro print_hex\n        jsr debug_hex\n.endmacro               ; print_hex\n\n.macro print_str_nl str\n        print_str str\n        jsr debug_nl\n.endmacro               ; print_str_nl\n\n.macro print_str_hex str\n        print_str str\n        jsr debug_hex\n.endmacro               ; print_str_hex\n\n.macro print_hex_nl\n        jsr debug_hex\n        jsr debug_nl\n.endmacro               ; print_hex_nl\n\n.macro print_str_hex_nl str\n        print_str str\n        jsr debug_hex\n        jsr debug_nl\n.endmacro               ; print_str_hex_nl\n\n.macro print_nl\n        jsr debug_nl\n.endmacro               ; print_nl\n\n.macro print_long arg\n        save_regs\n        lda #<percent_08lx\n        ldx #>percent_08lx\n        jsr pushax\n        lda arg+2\n        sta sreg\n        lda arg+3\n        sta sreg+1\n        lda arg\n        ldx arg+1\n        jsr pusheax\n        ldy #6\n        jsr _printf\n        restore_regs\n.endmacro               ; print_log\n\n        .data\npercent_08lx:\n        .asciiz \"[%08lx]\"\n        .code\n\n.macro print_word arg\n        save_regs\n        lda #<percent_04x\n        ldx #>percent_04x\n        jsr pushax\n        lda arg\n        ldx arg+1\n        jsr pushax\n        ldy #4\n        jsr _printf\n        restore_regs\n.endmacro               ; print_log\n\n        .data\npercent_04x:\n        .asciiz \"[%04x]\"\n        .code\n\n.proc debug_str\n        save_regs\n        lda #<percent_s\n        ldx #>percent_s\n        jsr pushax\n        lda save_a\n        ldx save_x\n        jsr pushax\n        ldy #4\n        jsr _printf\n        restore_regs\n        rts\n\n        .data\npercent_s:\n        .asciiz \"%s\"\n        .code\n\n.endproc                ; debug_str\n\n.proc debug_hex\n        save_regs\n        lda #<percent_x\n        ldx #>percent_x\n        jsr pushax\n        lda save_a\n        jsr pusha0\n        ldy #4\n        jsr _printf\n        restore_regs\n        rts\n\n        .data\npercent_x:\n        .asciiz \"%02X\"\n        .code\n\n.endproc                ; debug_hex\n\n.proc debug_nl\n        save_regs\n        lda #<newline\n        ldx #>newline\n        jsr pushax\n        ldy #2\n        jsr _printf\n        restore_regs\n        rts\n\n        .data\nnewline:\n        .byte $0a, $00\n        .code\n\n.endproc                ; debug_nl\n"
  },
  {
    "path": "tools/make_charprops.pl",
    "content": "#!/usr/bin/perl -w\n\n# JSON65 - A JSON parser for the 6502 microprocessor.\n#\n# https://github.com/ppelleti/json65\n#\n# Copyright © 2018 Patrick Pelletier\n#\n# This software is provided 'as-is', without any express or implied\n# warranty.  In no event will the authors be held liable for any damages\n# arising from the use of this software.\n#\n# Permission is granted to anyone to use this software for any purpose,\n# including commercial applications, and to alter it and redistribute it\n# freely, subject to the following restrictions:\n#\n# 1. The origin of this software must not be misrepresented; you must not\n#    claim that you wrote the original software. If you use this software\n#    in a product, an acknowledgment in the product documentation would be\n#    appreciated but is not required.\n# 2. Altered source versions must be plainly marked as such, and must not be\n#    misrepresented as being the original software.\n# 3. This notice may not be removed or altered from any source distribution.\n\nmy ($ws, $str, $lit, $int, $num) =\n    qw(prop_ws prop_str prop_lit prop_int prop_num);\nmy ($lsq, $lcur, $rsq, $rcur, $colon, $comma, $quote) =\n    qw(sc_lsq sc_lcur sc_rsq sc_rcur sc_colon sc_comma sc_quote);\n\nmy @props = ();\n\nfor (my $i = 0; $i < 128; $i++) {\n    push @props, [];\n}\n\nforeach my $c (0x20, 0x09, 0x0a, 0x0d) {\n    push $props[$c], $ws;\n}\n\nfor (my $c = 32; $c < 128; $c++) {\n    push $props[$c], $str;\n}\n\nforeach my $c (split (//, \"aeflnrstu\")) {\n    push $props[ord($c)], $lit;\n}\n\nforeach my $c (split (//, \"-0123456789\")) {\n    push $props[ord($c)], $int;\n    push $props[ord($c)], $num;\n}\n\nforeach my $c (split (//, \".eE+\")) {\n    push $props[ord($c)], $num;\n}\n\npush $props[ord(\"[\")], $lsq;\npush $props[ord(\"{\")], $lcur;\npush $props[ord(\"]\")], $rsq;\npush $props[ord(\"}\")], $rcur;\npush $props[ord(\":\")], $colon;\npush $props[ord(\",\")], $comma;\npush $props[ord(\"\\\"\")], $quote;\n\nprint \"charprops:\\n\";\nmy $c = 0;\nforeach my $prop (@props) {\n    print \"        .byte \";\n    my $x = join (\"|\", @$prop);\n    if ($x eq \"\") {\n        $x = \"0\";\n    }\n    printf \"%-27s\", $x;\n    print \"; \";\n    if ($c > 32 and $c < 127) {\n        print chr($c);\n    } else {\n        printf \"%s%02x\", '$', $c;\n    }\n    print \"\\n\";\n    $c++;\n}\n"
  },
  {
    "path": "tools/make_dispatch.pl",
    "content": "#!/usr/bin/perl -w\n\n# JSON65 - A JSON parser for the 6502 microprocessor.\n#\n# https://github.com/ppelleti/json65\n#\n# Copyright © 2018 Patrick Pelletier\n#\n# This software is provided 'as-is', without any express or implied\n# warranty.  In no event will the authors be held liable for any damages\n# arising from the use of this software.\n#\n# Permission is granted to anyone to use this software for any purpose,\n# including commercial applications, and to alter it and redistribute it\n# freely, subject to the following restrictions:\n#\n# 1. The origin of this software must not be misrepresented; you must not\n#    claim that you wrote the original software. If you use this software\n#    in a product, an acknowledgment in the product documentation would be\n#    appreciated but is not required.\n# 2. Altered source versions must be plainly marked as such, and must not be\n#    misrepresented as being the original software.\n# 3. This notice may not be removed or altered from any source distribution.\n\nuse strict;\n\nmy @schars =\n    qw(dt_none dt_lsq dt_lcur dt_rsq dt_rcur dt_colon dt_comma dt_quote);\n\nmy ($none, $lsq, $lcur, $rsq, $rcur, $colon, $comma, $quote) = @schars;\n\nmy ($rdy, $rdy_ca, $key, $key_co, $ncolon, $ncomma_ca, $ncomma_co, $done) =\n    (0, 1, 2, 3, 4, 5, 6, 7);\n\nmy ($perr, $ichar) =\n    qw(disp_parse_error disp_illegal_char);\nmy ($xstr, $xcolon, $xcomma) =\n    qw(disp_exp_string disp_exp_colon disp_exp_comma);\nmy ($xobj, $xarr) =\n    qw(disp_exp_obj_end disp_exp_array_end);\n\nmy ($sobj, $eobj) =\n    qw(disp_start_obj disp_end_obj);\nmy ($sarr, $earr, $sstr) =\n    qw(disp_start_array disp_end_array disp_start_string);\nmy ($comarr, $comobj, $dcolon) =\n    qw(disp_comma_array disp_comma_object disp_colon);\n\nmy %table = ();\n\nmy @default = ($perr, $perr, $perr, $perr, $perr, $perr, $perr, $perr);\n$default[$key] = $xstr;\n$default[$key_co] = $xstr;\n$default[$ncolon] = $xcolon;\n$default[$ncomma_ca] = $xcomma;\n$default[$ncomma_co] = $xcomma;\n\nforeach my $schar (@schars) {\n    $table{$schar} = [@default];\n}\n\n$table{$none} =\n    [$ichar, $ichar, $ichar, $ichar, $ichar, $ichar, $ichar, $ichar];\n\nforeach my $state ($rdy_ca, $ncomma_ca) {\n    $table{$rcur}[$state] = $xarr;\n}\n\nforeach my $state ($key_co, $ncomma_co) {\n    $table{$rsq}[$state] = $xobj;\n}\n\nforeach my $state ($rdy, $rdy_ca) {\n    $table{$lsq}[$state]   = $sarr;\n    $table{$lcur}[$state]  = $sobj;\n    $table{$quote}[$state] = $sstr;\n}\n\n$table{$rsq}[$rdy_ca]   = $earr;\n$table{$quote}[$key] = $sstr;\n$table{$quote}[$key_co] = $sstr;\n$table{$rcur}[$key_co]  = $eobj;\n$table{$colon}[$ncolon] = $dcolon;\n$table{$comma}[$ncomma_ca] = $comarr;\n$table{$comma}[$ncomma_co] = $comobj;\n$table{$rsq}[$ncomma_ca] = $earr;\n$table{$rcur}[$ncomma_co] = $eobj;\n\nforeach my $schar (@schars) {\n    printf \".define %-8s\", $schar;\n    my $sep = \" \";\n    foreach my $func (@{$table{$schar}}) {\n        print $sep, $func, \"-1\";\n        $sep = \",\";\n    }\n    print \"\\n\";\n}\n"
  },
  {
    "path": "tools/random-json.hs",
    "content": "#!/usr/bin/env stack\n-- stack --resolver lts-12.6 --install-ghc runghc --package MonadRandom\n\n{-\n  JSON65 - A JSON parser for the 6502 microprocessor.\n\n  https://github.com/ppelleti/json65\n\n  Copyright © 2018 Patrick Pelletier\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n-}\n\nimport Control.Monad\nimport Control.Monad.Random.Strict\nimport Data.Char\nimport Data.List\nimport Text.Printf\n\nrandomLiteral :: MonadRandom m => m String\nrandomLiteral = do\n  x <- getRandomR (0, 2)\n  return $ [\"null\", \"false\", \"true\"] !! x\n\nrandomInteger :: MonadRandom m => m String\nrandomInteger = do\n  x <- getRandomR (-4294967296, 4294967296)\n  return $ show (x :: Integer)\n\nrandomNumber :: MonadRandom m => m String\nrandomNumber = do\n  x <- getRandom\n  return $ show (x :: Float)\n\nescapes :: [(Char, Char)]\nescapes =\n  [ ('\\\"', '\\\"')\n  , ('\\\\', '\\\\')\n  , ('\\b', 'b')\n  , ('\\f', 'f')\n  , ('\\n', 'n')\n  , ('\\r', 'r')\n  , ('\\t', 't')\n  ]\n\nrandomChar :: MonadRandom m => m String\nrandomChar = do\n  x <- getRandomR (1, 126)\n  let c = chr x\n      esc = c `lookup` escapes\n  case esc of\n    (Just c') -> return $ ['\\\\', c']\n    _ -> if x < 32\n         then return $ printf \"\\\\u%04x\" x\n         else return [c]\n\nrandomString :: MonadRandom m => m String\nrandomString = do\n  len <- getRandomR (0, 6)\n  chars <- replicateM len randomChar\n  return $ \"\\\"\" ++ concat chars ++ \"\\\"\"\n\nrandomArray :: MonadRandom m => Int -> m String\nrandomArray level = do\n  len <- getRandomR (0, 5)\n  values <- replicateM len (randomValue (level - 1))\n  return $ \"[\" ++ intercalate \",\" values ++ \"]\"\n\nmkPair :: String -> String -> String\nmkPair k v = k ++ \":\" ++ v\n\nrandomObject :: MonadRandom m => Int -> m String\nrandomObject level = do\n  len <- getRandomR (0, 5)\n  keys <- replicateM len randomString\n  values <- replicateM len (randomValue (level - 1))\n  let pairs = zipWith mkPair keys values\n  return $ \"{\" ++ intercalate \",\" pairs ++ \"}\"\n\nrandomValue :: MonadRandom m => Int -> m String\nrandomValue level = do\n  let highest = if level < 0 then 3 else 5\n  x <- getRandomR (0, highest)\n  case x of\n    4 -> randomArray level\n    5 -> randomObject level\n    _ -> [randomLiteral, randomInteger, randomNumber, randomString] !! x\n\nrandomArrayOrObject :: MonadRandom m => Int -> m String\nrandomArrayOrObject level = do\n  x <- getRandom\n  if x then randomArray level else randomObject level\n\ngenerateStuff :: Int -> IO ()\ngenerateStuff level = do\n  str <- randomArrayOrObject level\n  when (length str < 2046) $ do\n    putStrLn str\n    generateStuff (level + 1)\n\nmain :: IO ()\nmain = generateStuff 0\n"
  }
]