Table of Contents 1. [[#concepts--conventions][Concepts & Conventions]] - [[#parsers][Parsers]] - [[#on-the-terms-accept-succeed-fail-and-result][On the Terms "Accept", "Succeed", "Fail" and "Result"]] - [[#naming-conventions][Naming Conventions]] 2. [[#getting-started-finally][Getting Started (Finally)]] 3. [[#jumping-in-with-whitespace][Jumping in with Whitespace]] 4. [[#mapping-results-to-true-false-and-null][Mapping Results to true, false, and null]] 5. [[#the-fundamental-bind-parsing-numbers][The Fundamental < (let ((string " ")) (parse string PZ-JSON> (let ((string " ")) (parse string PZ-JSON> #+END_SRC So what is going on? The combinators =< (parse "hey dude" (< #+END_SRC The parser =(< (parse "hey dude" (< #+END_SRC The parse resulted in failure (indicated by a second return value of =NIL=) because, though /dude/ appeared in the input, it was not at the beginning of the stream. At this point it seems clear that you will will want to define parsers that look something like this: #+BEGIN_SRC lisp (< (parse "hey dude" (< PZ-JSON> #+END_SRC Ah! Much easier to understand. You just apply =#'string-upcase= to the result of =(< ; #'(LAMBDA (NULL) :NULL) ; ; caught STYLE-WARNING: ; The variable NULL is defined but never used. ; ; compilation unit finished ; caught 1 STYLE-WARNING condition PZ-JSON> (parse "null" #+END_SRC Hmm everything works, but the compiler isn't happy. It is reporting a warning that a variable is being defined but not used. You could get rid of this by doing something like, for example =(declare (ignore null))=, for each of the above parser definitions, but it isn't necessary: =parzival= supplies a mapping variant called =< (parse "abcd" (< PZ-JSON> (parse "abcd" (< PZ-JSON> #+END_SRC Both parses succeed, indicated by the =T= as the second return value, but the second parse would have failed if it were not made optional using =< (< PZ-JSON> (parse "?a 7" PZ-JSON> (parse "?z 7" PZ-JSON> #+END_SRC What is going on here? The above example, while illustrative, is perhaps a bit hard to look at. Stay strong - relief will soon be found when =< (parse "-234.443e-4" PZ-JSON> (parse "-234.443e4" PZ-JSON> (parse "4.443E+3" PZ-JSON> (parse "0.443E+3" PZ-JSON> (parse "00001.443E+3" PZ-JSON> #+END_SRC In the very last REPL example, you see that = (parse "aaba" (< PZ-JSON> (parse "aaba" (< #+END_SRC And the =< (parse "\"ab\\u6211cd moo \\n\"" PZ-JSON> (parse "\"ab\\u0123Fcd\"" PZ-JSON> (parse "\"they call me Colin \\\"Parse Master\\\" Okay\"" PZ-JSON> #+END_SRC ** Recursive Parsers You're in the home stretch! You've defined parsers for all of the primitive value types, and now only the complex types remain. And here is where you encounter a new and interesting challenge. Looking at the JSON definition, you notice two things. 1) =value=, representing any valid JSON value, is define din terms of =object= and =array=. 2) But =object= and =array= are both defined in terms of =value=. That's right! It's time for recursive parser definitions. So, without having defined = (parse "{\"a\" : 10 , \"b\" : 3 }" PZ-JSON> (parse "{ \"name\" : \"colin\", \"hobbies\" : [\"lisp\" , \"parsing\" ] , \"features\" : { \"head\" : \"round\", \"eyes\" : 2} }" PZ-JSON> #+END_SRC *** Parsing JSON Files Here is how you would parse some JSON from a file: #+BEGIN_SRC lisp PZ-JSON> (with-open-file (file-input "examples/foo.json") (let ((rp-stream (make-instance 'replay-streams:character-input-replay-stream :source file-input))) (parse rp-stream PZ-JSON> #+END_SRC For the moment, parsers only work on instances of [[https://github.com/cbeo/replay-streams][replay-streams]]. If you pass raw text to the =parse= function for its =STREAM= argument, then you must also pass a =T= into its third optional argument position. Otherwise the stream is assumed to be a =replay-stream=. *** Problems to Puzzle Out 1. Association Lists may or may not be the most appropriate data structure for the representation of JSON objects. How could you change the =