mirror of
https://github.com/boostorg/parser.git
synced 2026-02-15 13:22:13 +00:00
403 lines
62 KiB
HTML
403 lines
62 KiB
HTML
<html>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
<title>Parsing JSON</title>
|
||
<link rel="stylesheet" href="../../boostbook.css" type="text/css">
|
||
<meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
|
||
<link rel="home" href="../../index.html" title="Chapter 1. Boost.Parser">
|
||
<link rel="up" href="../extended_examples.html" title="Extended Examples">
|
||
<link rel="prev" href="../extended_examples.html" title="Extended Examples">
|
||
<link rel="next" href="parsing_json_with_callbacks.html" title="Parsing JSON With Callbacks">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
</head>
|
||
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
||
<div class="spirit-nav">
|
||
<a accesskey="p" href="../extended_examples.html"><img src="../../images/prev.png" alt="Prev"></a><a accesskey="u" href="../extended_examples.html"><img src="../../images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../images/home.png" alt="Home"></a><a accesskey="n" href="parsing_json_with_callbacks.html"><img src="../../images/next.png" alt="Next"></a>
|
||
</div>
|
||
<div class="section">
|
||
<div class="titlepage"><div><div><h3 class="title">
|
||
<a name="boost_parser.extended_examples.parsing_json"></a><a class="link" href="parsing_json.html" title="Parsing JSON">Parsing
|
||
JSON</a>
|
||
</h3></div></div></div>
|
||
<p>
|
||
This is a conforming JSON parser. It passes all the required tests in the
|
||
<a href="https://github.com/nst/JSONTestSuite" target="_top">JSON Test Suite</a>,
|
||
and all but 5 of the optional ones. Notice that the actual parsing bits are
|
||
only about 150 lines of code.
|
||
</p>
|
||
<p>
|
||
</p>
|
||
<pre class="programlisting"><span class="comment">// This header includes a type called json::value that acts as a</span>
|
||
<span class="comment">// Javascript-like polymorphic value type.</span>
|
||
<span class="preprocessor">#include</span> <span class="string">"json.hpp"</span>
|
||
|
||
<span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">boost</span><span class="special">/</span><span class="identifier">parser</span><span class="special">/</span><span class="identifier">parser</span><span class="special">.</span><span class="identifier">hpp</span><span class="special">></span>
|
||
|
||
<span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">fstream</span><span class="special">></span>
|
||
<span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">vector</span><span class="special">></span>
|
||
<span class="preprocessor">#include</span> <span class="special"><</span><span class="identifier">climits</span><span class="special">></span>
|
||
|
||
|
||
<span class="keyword">namespace</span> <span class="identifier">json</span> <span class="special">{</span>
|
||
|
||
<span class="keyword">namespace</span> <span class="identifier">bp</span> <span class="special">=</span> <span class="special">::</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">parser</span><span class="special">;</span>
|
||
<span class="keyword">using</span> <span class="keyword">namespace</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">literals</span><span class="special">;</span>
|
||
|
||
<span class="comment">// The JSON spec imposes a limit on how deeply JSON data structures are</span>
|
||
<span class="comment">// allowed to nest. This exception is thrown when that limit is exceeded</span>
|
||
<span class="comment">// during the parse.</span>
|
||
<span class="keyword">template</span><span class="special"><</span><span class="keyword">typename</span> <span class="identifier">Iter</span><span class="special">></span>
|
||
<span class="keyword">struct</span> <span class="identifier">excessive_nesting</span> <span class="special">:</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">runtime_error</span>
|
||
<span class="special">{</span>
|
||
<span class="identifier">excessive_nesting</span><span class="special">(</span><span class="identifier">Iter</span> <span class="identifier">it</span><span class="special">)</span> <span class="special">:</span>
|
||
<span class="identifier">runtime_error</span><span class="special">(</span><span class="string">"excessive_nesting"</span><span class="special">),</span>
|
||
<span class="identifier">iter</span><span class="special">(</span><span class="identifier">it</span><span class="special">)</span>
|
||
<span class="special">{}</span>
|
||
<span class="identifier">Iter</span> <span class="identifier">iter</span><span class="special">;</span>
|
||
<span class="special">};</span>
|
||
|
||
|
||
<span class="comment">// The only globals we need to parse JSON are: "How many data structures</span>
|
||
<span class="comment">// deep are we?", and "What is the limit of open data structures</span>
|
||
<span class="comment">// allowed?".</span>
|
||
<span class="keyword">struct</span> <span class="identifier">global_state</span>
|
||
<span class="special">{</span>
|
||
<span class="keyword">int</span> <span class="identifier">recursive_open_count</span> <span class="special">=</span> <span class="number">0</span><span class="special">;</span>
|
||
<span class="keyword">int</span> <span class="identifier">max_recursive_open_count</span> <span class="special">=</span> <span class="number">0</span><span class="special">;</span>
|
||
<span class="special">};</span>
|
||
|
||
<span class="comment">// When matching paired UTF-16 surrogates, we need to track a bit of state</span>
|
||
<span class="comment">// between matching the first and second UTF-16 code units: namely, the</span>
|
||
<span class="comment">// value of the first code unit.</span>
|
||
<span class="keyword">struct</span> <span class="identifier">double_escape_locals</span>
|
||
<span class="special">{</span>
|
||
<span class="keyword">int</span> <span class="identifier">first_surrogate</span> <span class="special">=</span> <span class="number">0</span><span class="special">;</span>
|
||
<span class="special">};</span>
|
||
|
||
|
||
<span class="comment">// Here are all the rules declared. I've given them names that are</span>
|
||
<span class="comment">// end-user friendly, so that if there is a parse error, you get a message</span>
|
||
<span class="comment">// like "expected four hexadecimal digits here:", instead of "expected</span>
|
||
<span class="comment">// hex_4 here:".</span>
|
||
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">ws</span><span class="special">></span> <span class="keyword">const</span> <span class="identifier">ws</span> <span class="special">=</span> <span class="string">"whitespace"</span><span class="special">;</span>
|
||
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">string_char</span><span class="special">,</span> <span class="identifier">uint32_t</span><span class="special">></span> <span class="keyword">const</span> <span class="identifier">string_char</span> <span class="special">=</span>
|
||
<span class="string">"code point (code points <= U+001F must be escaped)"</span><span class="special">;</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">four_hex_digits</span><span class="special">,</span> <span class="identifier">uint32_t</span><span class="special">></span> <span class="keyword">const</span> <span class="identifier">hex_4</span> <span class="special">=</span>
|
||
<span class="string">"four hexadecimal digits"</span><span class="special">;</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">escape_seq</span><span class="special">,</span> <span class="identifier">uint32_t</span><span class="special">></span> <span class="keyword">const</span> <span class="identifier">escape_seq</span> <span class="special">=</span>
|
||
<span class="string">"\\uXXXX hexadecimal escape sequence"</span><span class="special">;</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">escape_double_seq</span><span class="special">,</span> <span class="identifier">uint32_t</span><span class="special">,</span> <span class="identifier">double_escape_locals</span><span class="special">></span> <span class="keyword">const</span>
|
||
<span class="identifier">escape_double_seq</span> <span class="special">=</span> <span class="string">"\\uXXXX hexadecimal escape sequence"</span><span class="special">;</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">single_escaped_char</span><span class="special">,</span> <span class="identifier">uint32_t</span><span class="special">></span> <span class="keyword">const</span> <span class="identifier">single_escaped_char</span> <span class="special">=</span>
|
||
<span class="string">"'\"', '\\', '/', 'b', 'f', 'n', 'r', or 't'"</span><span class="special">;</span>
|
||
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">null</span><span class="special">,</span> <span class="identifier">value</span><span class="special">></span> <span class="keyword">const</span> <span class="identifier">null</span> <span class="special">=</span> <span class="string">"null"</span><span class="special">;</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">string</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">></span> <span class="keyword">const</span> <span class="identifier">string</span> <span class="special">=</span> <span class="string">"string"</span><span class="special">;</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">number</span><span class="special">,</span> <span class="keyword">double</span><span class="special">></span> <span class="keyword">const</span> <span class="identifier">number</span> <span class="special">=</span> <span class="string">"number"</span><span class="special">;</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">object_element</span><span class="special">,</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">parser</span><span class="special">::</span><span class="identifier">tuple</span><span class="special"><</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">,</span> <span class="identifier">value</span><span class="special">>></span> <span class="keyword">const</span>
|
||
<span class="identifier">object_element</span> <span class="special">=</span> <span class="string">"object-element"</span><span class="special">;</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">object_tag</span><span class="special">,</span> <span class="identifier">value</span><span class="special">></span> <span class="keyword">const</span> <span class="identifier">object_p</span> <span class="special">=</span> <span class="string">"object"</span><span class="special">;</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">array_tag</span><span class="special">,</span> <span class="identifier">value</span><span class="special">></span> <span class="keyword">const</span> <span class="identifier">array_p</span> <span class="special">=</span> <span class="string">"array"</span><span class="special">;</span>
|
||
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special"><</span><span class="keyword">class</span> <span class="identifier">value_tag</span><span class="special">,</span> <span class="identifier">value</span><span class="special">></span> <span class="keyword">const</span> <span class="identifier">value_p</span> <span class="special">=</span> <span class="string">"value"</span><span class="special">;</span>
|
||
|
||
|
||
|
||
<span class="comment">// JSON limits whitespace to just these four characters.</span>
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">ws_def</span> <span class="special">=</span> <span class="char">'\x09'</span><span class="identifier">_l</span> <span class="special">|</span> <span class="char">'\x0a'</span> <span class="special">|</span> <span class="char">'\x0d'</span> <span class="special">|</span> <span class="char">'\x20'</span><span class="special">;</span>
|
||
|
||
<span class="comment">// Since our json object representation, json::value, is polymorphic, and</span>
|
||
<span class="comment">// since its default-constructed state represents the JSON value "null",</span>
|
||
<span class="comment">// we need to tell a json::value that it is an object (similar to a map)</span>
|
||
<span class="comment">// before we start inserting values into it. That's why we need</span>
|
||
<span class="comment">// object_init.</span>
|
||
<span class="keyword">auto</span> <span class="identifier">object_init</span> <span class="special">=</span> <span class="special">[](</span><span class="keyword">auto</span> <span class="special">&</span> <span class="identifier">ctx</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="keyword">auto</span> <span class="special">&</span> <span class="identifier">globals</span> <span class="special">=</span> <span class="identifier">_globals</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">);</span>
|
||
<span class="keyword">if</span> <span class="special">(</span><span class="identifier">globals</span><span class="special">.</span><span class="identifier">max_recursive_open_count</span> <span class="special"><</span> <span class="special">++</span><span class="identifier">globals</span><span class="special">.</span><span class="identifier">recursive_open_count</span><span class="special">)</span>
|
||
<span class="keyword">throw</span> <span class="identifier">excessive_nesting</span><span class="special">(</span><span class="identifier">_where</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">).</span><span class="identifier">begin</span><span class="special">());</span>
|
||
<span class="identifier">_val</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">)</span> <span class="special">=</span> <span class="identifier">object</span><span class="special">();</span>
|
||
<span class="special">};</span>
|
||
|
||
<span class="comment">// We need object_insert because we can't just insert into the json::value</span>
|
||
<span class="comment">// itself. The json::value does not have an insert() member, because if</span>
|
||
<span class="comment">// it is currently holding a number, that makes no sense. So, for a</span>
|
||
<span class="comment">// json::value x, we need to call get<object>(x) to get the object</span>
|
||
<span class="comment">// interface.</span>
|
||
<span class="keyword">auto</span> <span class="identifier">object_insert</span> <span class="special">=</span> <span class="special">[](</span><span class="keyword">auto</span> <span class="special">&</span> <span class="identifier">ctx</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="identifier">value</span> <span class="special">&</span> <span class="identifier">v</span> <span class="special">=</span> <span class="identifier">_val</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">);</span>
|
||
<span class="identifier">get</span><span class="special"><</span><span class="identifier">object</span><span class="special">>(</span><span class="identifier">v</span><span class="special">).</span><span class="identifier">insert</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">make_pair</span><span class="special">(</span>
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">move</span><span class="special">(</span><span class="identifier">_attr</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">))[</span><span class="number">0</span><span class="identifier">_c</span><span class="special">],</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">move</span><span class="special">(</span><span class="identifier">_attr</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">)[</span><span class="number">1</span><span class="identifier">_c</span><span class="special">])));</span>
|
||
<span class="special">};</span>
|
||
|
||
<span class="comment">// These are the array analogues of the object semantic actions above.</span>
|
||
<span class="keyword">auto</span> <span class="identifier">array_init</span> <span class="special">=</span> <span class="special">[](</span><span class="keyword">auto</span> <span class="special">&</span> <span class="identifier">ctx</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="keyword">auto</span> <span class="special">&</span> <span class="identifier">globals</span> <span class="special">=</span> <span class="identifier">_globals</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">);</span>
|
||
<span class="keyword">if</span> <span class="special">(</span><span class="identifier">globals</span><span class="special">.</span><span class="identifier">max_recursive_open_count</span> <span class="special"><</span> <span class="special">++</span><span class="identifier">globals</span><span class="special">.</span><span class="identifier">recursive_open_count</span><span class="special">)</span>
|
||
<span class="keyword">throw</span> <span class="identifier">excessive_nesting</span><span class="special">(</span><span class="identifier">_where</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">).</span><span class="identifier">begin</span><span class="special">());</span>
|
||
<span class="identifier">_val</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">)</span> <span class="special">=</span> <span class="identifier">array</span><span class="special">();</span>
|
||
<span class="special">};</span>
|
||
<span class="keyword">auto</span> <span class="identifier">array_append</span> <span class="special">=</span> <span class="special">[](</span><span class="keyword">auto</span> <span class="special">&</span> <span class="identifier">ctx</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="identifier">value</span> <span class="special">&</span> <span class="identifier">v</span> <span class="special">=</span> <span class="identifier">_val</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">);</span>
|
||
<span class="identifier">get</span><span class="special"><</span><span class="identifier">array</span><span class="special">>(</span><span class="identifier">v</span><span class="special">).</span><span class="identifier">push_back</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">move</span><span class="special">(</span><span class="identifier">_attr</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">)));</span>
|
||
<span class="special">};</span>
|
||
|
||
<span class="comment">// escape_double_seq is used to match pairs of UTF-16 surrogates that form</span>
|
||
<span class="comment">// a single code point. So, after matching one UTF-16 code unit c, we</span>
|
||
<span class="comment">// only want to keep going if c is a lead/high surrogate.</span>
|
||
<span class="keyword">auto</span> <span class="identifier">first_hex_escape</span> <span class="special">=</span> <span class="special">[](</span><span class="keyword">auto</span> <span class="special">&</span> <span class="identifier">ctx</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="keyword">auto</span> <span class="special">&</span> <span class="identifier">locals</span> <span class="special">=</span> <span class="identifier">_locals</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">);</span>
|
||
<span class="identifier">uint32_t</span> <span class="keyword">const</span> <span class="identifier">cu</span> <span class="special">=</span> <span class="identifier">_attr</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">);</span>
|
||
<span class="keyword">if</span> <span class="special">(!</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">parser</span><span class="special">::</span><span class="identifier">detail</span><span class="special">::</span><span class="identifier">text</span><span class="special">::</span><span class="identifier">high_surrogate</span><span class="special">(</span><span class="identifier">cu</span><span class="special">))</span>
|
||
<span class="identifier">_pass</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">)</span> <span class="special">=</span> <span class="keyword">false</span><span class="special">;</span> <span class="comment">// Not a high surrogate; explicitly fail the parse.</span>
|
||
<span class="keyword">else</span>
|
||
<span class="identifier">locals</span><span class="special">.</span><span class="identifier">first_surrogate</span> <span class="special">=</span> <span class="identifier">cu</span><span class="special">;</span> <span class="comment">// Save this initial code unit for later.</span>
|
||
<span class="special">};</span>
|
||
<span class="comment">// This is also used in escape_double_seq. When we get to this action, we</span>
|
||
<span class="comment">// know we've already matched a high surrogate, and so this one had better</span>
|
||
<span class="comment">// be a low surrogate, or we have a (local) parse failure.</span>
|
||
<span class="keyword">auto</span> <span class="identifier">second_hex_escape</span> <span class="special">=</span> <span class="special">[](</span><span class="keyword">auto</span> <span class="special">&</span> <span class="identifier">ctx</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="keyword">auto</span> <span class="special">&</span> <span class="identifier">locals</span> <span class="special">=</span> <span class="identifier">_locals</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">);</span>
|
||
<span class="identifier">uint32_t</span> <span class="keyword">const</span> <span class="identifier">cu</span> <span class="special">=</span> <span class="identifier">_attr</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">);</span>
|
||
<span class="keyword">if</span> <span class="special">(!</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">parser</span><span class="special">::</span><span class="identifier">detail</span><span class="special">::</span><span class="identifier">text</span><span class="special">::</span><span class="identifier">low_surrogate</span><span class="special">(</span><span class="identifier">cu</span><span class="special">))</span> <span class="special">{</span>
|
||
<span class="identifier">_pass</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">)</span> <span class="special">=</span> <span class="keyword">false</span><span class="special">;</span> <span class="comment">// Not a low surrogate; explicitly fail the parse.</span>
|
||
<span class="special">}</span> <span class="keyword">else</span> <span class="special">{</span>
|
||
<span class="comment">// Success! Write to the rule's attribute the code point that the</span>
|
||
<span class="comment">// first and second code points form.</span>
|
||
<span class="identifier">uint32_t</span> <span class="keyword">const</span> <span class="identifier">high_surrogate_min</span> <span class="special">=</span> <span class="number">0xd800</span><span class="special">;</span>
|
||
<span class="identifier">uint32_t</span> <span class="keyword">const</span> <span class="identifier">low_surrogate_min</span> <span class="special">=</span> <span class="number">0xdc00</span><span class="special">;</span>
|
||
<span class="identifier">uint32_t</span> <span class="keyword">const</span> <span class="identifier">surrogate_offset</span> <span class="special">=</span>
|
||
<span class="number">0x10000</span> <span class="special">-</span> <span class="special">(</span><span class="identifier">high_surrogate_min</span> <span class="special"><<</span> <span class="number">10</span><span class="special">)</span> <span class="special">-</span> <span class="identifier">low_surrogate_min</span><span class="special">;</span>
|
||
<span class="identifier">uint32_t</span> <span class="keyword">const</span> <span class="identifier">first_cu</span> <span class="special">=</span> <span class="identifier">locals</span><span class="special">.</span><span class="identifier">first_surrogate</span><span class="special">;</span>
|
||
<span class="identifier">_val</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">)</span> <span class="special">=</span> <span class="special">(</span><span class="identifier">first_cu</span> <span class="special"><<</span> <span class="number">10</span><span class="special">)</span> <span class="special">+</span> <span class="identifier">cu</span> <span class="special">+</span> <span class="identifier">surrogate_offset</span><span class="special">;</span>
|
||
<span class="special">}</span>
|
||
<span class="special">};</span>
|
||
|
||
<span class="comment">// This is the verbose form of declaration for the integer and unsigned</span>
|
||
<span class="comment">// integer parsers int_parser and uint_parser. In this case, we don't</span>
|
||
<span class="comment">// want to use boost::parser::hex directly, since it has a variable number</span>
|
||
<span class="comment">// of digits. We want to match exactly 4 digits, and this is how we</span>
|
||
<span class="comment">// declare a hexadecimal parser that matches exactly 4.</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">parser_interface</span><span class="special"><</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">uint_parser</span><span class="special"><</span><span class="identifier">uint32_t</span><span class="special">,</span> <span class="number">16</span><span class="special">,</span> <span class="number">4</span><span class="special">,</span> <span class="number">4</span><span class="special">>></span> <span class="keyword">const</span> <span class="identifier">hex_4_def</span><span class="special">;</span>
|
||
|
||
<span class="comment">// We use > here instead of >>, because once we see \u, we know that</span>
|
||
<span class="comment">// exactly four hex digits must follow -- no other production rule starts</span>
|
||
<span class="comment">// with \u.</span>
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">escape_seq_def</span> <span class="special">=</span> <span class="string">"\\u"</span> <span class="special">></span> <span class="identifier">hex_4</span><span class="special">;</span>
|
||
|
||
<span class="comment">// This uses the actions above and the simpler rule escape_seq to find</span>
|
||
<span class="comment">// matched UTF-16 surrogate pairs.</span>
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">escape_double_seq_def</span> <span class="special">=</span>
|
||
<span class="identifier">escape_seq</span><span class="special">[</span><span class="identifier">first_hex_escape</span><span class="special">]</span> <span class="special">>></span> <span class="identifier">escape_seq</span><span class="special">[</span><span class="identifier">second_hex_escape</span><span class="special">];</span>
|
||
|
||
<span class="comment">// This symbol table recognizes each character that can appear right after</span>
|
||
<span class="comment">// an escaping backslash, and, if it finds one, produces the associated</span>
|
||
<span class="comment">// code point as its attribute.</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">symbols</span><span class="special"><</span><span class="identifier">uint32_t</span><span class="special">></span> <span class="keyword">const</span> <span class="identifier">single_escaped_char_def</span> <span class="special">=</span> <span class="special">{</span>
|
||
<span class="special">{</span><span class="string">"\""</span><span class="special">,</span> <span class="number">0x0022u</span><span class="special">},</span>
|
||
<span class="special">{</span><span class="string">"\\"</span><span class="special">,</span> <span class="number">0x005cu</span><span class="special">},</span>
|
||
<span class="special">{</span><span class="string">"/"</span><span class="special">,</span> <span class="number">0x002fu</span><span class="special">},</span>
|
||
<span class="special">{</span><span class="string">"b"</span><span class="special">,</span> <span class="number">0x0008u</span><span class="special">},</span>
|
||
<span class="special">{</span><span class="string">"f"</span><span class="special">,</span> <span class="number">0x000cu</span><span class="special">},</span>
|
||
<span class="special">{</span><span class="string">"n"</span><span class="special">,</span> <span class="number">0x000au</span><span class="special">},</span>
|
||
<span class="special">{</span><span class="string">"r"</span><span class="special">,</span> <span class="number">0x000du</span><span class="special">},</span>
|
||
<span class="special">{</span><span class="string">"t"</span><span class="special">,</span> <span class="number">0x0009u</span><span class="special">}};</span>
|
||
|
||
<span class="comment">// A string may be a matched UTF-16 escaped surrogate pair, a single</span>
|
||
<span class="comment">// escaped UTF-16 code unit treated as a whole code point, a single</span>
|
||
<span class="comment">// escaped character like \f, or any other code point outside the range</span>
|
||
<span class="comment">// [0x0000u, 0x001fu]. Note that we had to put escape_double_seq before</span>
|
||
<span class="comment">// escape_seq. Otherwise, escape_seq would eat all the escape sequences</span>
|
||
<span class="comment">// before escape_double_seq could try to match them.</span>
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">string_char_def</span> <span class="special">=</span> <span class="identifier">escape_double_seq</span> <span class="special">|</span> <span class="identifier">escape_seq</span> <span class="special">|</span>
|
||
<span class="special">(</span><span class="char">'\\'</span><span class="identifier">_l</span> <span class="special">></span> <span class="identifier">single_escaped_char</span><span class="special">)</span> <span class="special">|</span>
|
||
<span class="special">(</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">cp</span> <span class="special">-</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">char_</span><span class="special">(</span><span class="number">0x0000u</span><span class="special">,</span> <span class="number">0x001fu</span><span class="special">));</span>
|
||
|
||
<span class="comment">// If we see the special token null, treat that as a default-constructed</span>
|
||
<span class="comment">// json::value. Note that we could have done this with a semantic action,</span>
|
||
<span class="comment">// but it is best to do everything you can without semantic actions;</span>
|
||
<span class="comment">// they're a lot of code.</span>
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">null_def</span> <span class="special">=</span> <span class="string">"null"</span> <span class="special">>></span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">attr</span><span class="special">(</span><span class="identifier">value</span><span class="special">());</span>
|
||
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">string_def</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">lexeme</span><span class="special">[</span><span class="char">'"'</span> <span class="special">>></span> <span class="special">*(</span><span class="identifier">string_char</span> <span class="special">-</span> <span class="char">'"'</span><span class="special">)</span> <span class="special">></span> <span class="char">'"'</span><span class="special">];</span>
|
||
|
||
<span class="comment">// Since the JSON format for numbers is not exactly what</span>
|
||
<span class="comment">// boost::parser::double_ accepts (double_ accepts too much), we need to</span>
|
||
<span class="comment">// parse a JSON number as a sequence of characters, and then pass the</span>
|
||
<span class="comment">// result to double_ to actually get the numeric value. This action does</span>
|
||
<span class="comment">// that. The parser uses boost::parser::raw to produce the subrange of</span>
|
||
<span class="comment">// the input that covers the number as an attribute, which is used here.</span>
|
||
<span class="keyword">auto</span> <span class="identifier">parse_double</span> <span class="special">=</span> <span class="special">[](</span><span class="keyword">auto</span> <span class="special">&</span> <span class="identifier">ctx</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">cp_range</span> <span class="special">=</span> <span class="identifier">_attr</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">);</span>
|
||
<span class="keyword">auto</span> <span class="identifier">cp_first</span> <span class="special">=</span> <span class="identifier">cp_range</span><span class="special">.</span><span class="identifier">begin</span><span class="special">();</span>
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">cp_last</span> <span class="special">=</span> <span class="identifier">cp_range</span><span class="special">.</span><span class="identifier">end</span><span class="special">();</span>
|
||
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">result</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">prefix_parse</span><span class="special">(</span><span class="identifier">cp_first</span><span class="special">,</span> <span class="identifier">cp_last</span><span class="special">,</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">double_</span><span class="special">);</span>
|
||
<span class="keyword">if</span> <span class="special">(</span><span class="identifier">result</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="identifier">_val</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">)</span> <span class="special">=</span> <span class="special">*</span><span class="identifier">result</span><span class="special">;</span>
|
||
<span class="special">}</span> <span class="keyword">else</span> <span class="special">{</span>
|
||
<span class="comment">// This would be more efficient if we used</span>
|
||
<span class="comment">// boost::container::small_vector, or std::inplace_vector from</span>
|
||
<span class="comment">// C++26.</span>
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special"><</span><span class="keyword">char</span><span class="special">></span> <span class="identifier">chars</span><span class="special">(</span><span class="identifier">cp_first</span><span class="special">,</span> <span class="identifier">cp_last</span><span class="special">);</span>
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">chars_first</span> <span class="special">=</span> <span class="special">&*</span><span class="identifier">chars</span><span class="special">.</span><span class="identifier">begin</span><span class="special">();</span>
|
||
<span class="keyword">auto</span> <span class="identifier">chars_last</span> <span class="special">=</span> <span class="identifier">chars_first</span> <span class="special">+</span> <span class="identifier">chars</span><span class="special">.</span><span class="identifier">size</span><span class="special">();</span>
|
||
<span class="identifier">_val</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">)</span> <span class="special">=</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">strtod</span><span class="special">(</span><span class="identifier">chars_first</span><span class="special">,</span> <span class="special">&</span><span class="identifier">chars_last</span><span class="special">);</span>
|
||
<span class="special">}</span>
|
||
<span class="special">};</span>
|
||
|
||
<span class="comment">// As indicated above, we want to match the specific formats JSON allows,</span>
|
||
<span class="comment">// and then re-parse the resulting matched range within the semantic</span>
|
||
<span class="comment">// action.</span>
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">number_def</span> <span class="special">=</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">raw</span><span class="special">[</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">lexeme</span>
|
||
<span class="special">[-</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">char_</span><span class="special">(</span><span class="char">'-'</span><span class="special">)</span> <span class="special">>></span>
|
||
<span class="special">(</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">char_</span><span class="special">(</span><span class="char">'1'</span><span class="special">,</span> <span class="char">'9'</span><span class="special">)</span> <span class="special">>></span> <span class="special">*</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">digit</span> <span class="special">|</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">char_</span><span class="special">(</span><span class="char">'0'</span><span class="special">))</span> <span class="special">>></span>
|
||
<span class="special">-(</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">char_</span><span class="special">(</span><span class="char">'.'</span><span class="special">)</span> <span class="special">>></span> <span class="special">+</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">digit</span><span class="special">)</span> <span class="special">>></span>
|
||
<span class="special">-(</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">char_</span><span class="special">(</span><span class="string">"eE"</span><span class="special">)</span> <span class="special">>></span> <span class="special">-</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">char_</span><span class="special">(</span><span class="string">"+-"</span><span class="special">)</span> <span class="special">>></span> <span class="special">+</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">digit</span><span class="special">)]]</span>
|
||
<span class="special">[</span><span class="identifier">parse_double</span><span class="special">];</span>
|
||
|
||
<span class="comment">// Note how, in the next three parsers, we turn off backtracking by using</span>
|
||
<span class="comment">// > instead of >>, once we know that there is no backtracking alternative</span>
|
||
<span class="comment">// that might match if we fail to match the next element. This produces</span>
|
||
<span class="comment">// much better error messages than if you always use >>.</span>
|
||
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">object_element_def</span> <span class="special">=</span> <span class="identifier">string</span> <span class="special">></span> <span class="char">':'</span> <span class="special">></span> <span class="identifier">value_p</span><span class="special">;</span>
|
||
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">object_p_def</span> <span class="special">=</span> <span class="char">'{'</span><span class="identifier">_l</span><span class="special">[</span><span class="identifier">object_init</span><span class="special">]</span> <span class="special">>></span>
|
||
<span class="special">-(</span><span class="identifier">object_element</span><span class="special">[</span><span class="identifier">object_insert</span><span class="special">]</span> <span class="special">%</span> <span class="char">','</span><span class="special">)</span> <span class="special">></span> <span class="char">'}'</span><span class="special">;</span>
|
||
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">array_p_def</span> <span class="special">=</span> <span class="char">'['</span><span class="identifier">_l</span><span class="special">[</span><span class="identifier">array_init</span><span class="special">]</span> <span class="special">>></span>
|
||
<span class="special">-(</span><span class="identifier">value_p</span><span class="special">[</span><span class="identifier">array_append</span><span class="special">]</span> <span class="special">%</span> <span class="char">','</span><span class="special">)</span> <span class="special">></span> <span class="char">']'</span><span class="special">;</span>
|
||
|
||
<span class="comment">// This is the top-level parser.</span>
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">value_p_def</span> <span class="special">=</span>
|
||
<span class="identifier">number</span> <span class="special">|</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">bool_</span> <span class="special">|</span> <span class="identifier">null</span> <span class="special">|</span> <span class="identifier">string</span> <span class="special">|</span> <span class="identifier">array_p</span> <span class="special">|</span> <span class="identifier">object_p</span><span class="special">;</span>
|
||
|
||
<span class="comment">// Here, we define all the rules we've declared above, which also connects</span>
|
||
<span class="comment">// each rule to its _def-suffixed parser.</span>
|
||
<span class="identifier">BOOST_PARSER_DEFINE_RULES</span><span class="special">(</span>
|
||
<span class="identifier">ws</span><span class="special">,</span>
|
||
<span class="identifier">hex_4</span><span class="special">,</span>
|
||
<span class="identifier">escape_seq</span><span class="special">,</span>
|
||
<span class="identifier">escape_double_seq</span><span class="special">,</span>
|
||
<span class="identifier">single_escaped_char</span><span class="special">,</span>
|
||
<span class="identifier">string_char</span><span class="special">,</span>
|
||
<span class="identifier">null</span><span class="special">,</span>
|
||
<span class="identifier">string</span><span class="special">,</span>
|
||
<span class="identifier">number</span><span class="special">,</span>
|
||
<span class="identifier">object_element</span><span class="special">,</span>
|
||
<span class="identifier">object_p</span><span class="special">,</span>
|
||
<span class="identifier">array_p</span><span class="special">,</span>
|
||
<span class="identifier">value_p</span><span class="special">);</span>
|
||
|
||
<span class="comment">// json::parse() takes a string_view as input. It takes an optional</span>
|
||
<span class="comment">// callback to use for error reporting, which defaults to a no-op that</span>
|
||
<span class="comment">// ignores all errors. It also takes an optional max recursion depth</span>
|
||
<span class="comment">// limit, which defaults to the one from the JSON spec, 512.</span>
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">optional</span><span class="special"><</span><span class="identifier">value</span><span class="special">></span> <span class="identifier">parse</span><span class="special">(</span>
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">string_view</span> <span class="identifier">str</span><span class="special">,</span>
|
||
<span class="identifier">diagnostic_function</span> <span class="identifier">errors_callback</span> <span class="special">=</span> <span class="identifier">diagnostic_function</span><span class="special">(),</span>
|
||
<span class="keyword">int</span> <span class="identifier">max_recursion</span> <span class="special">=</span> <span class="number">512</span><span class="special">)</span>
|
||
<span class="special">{</span>
|
||
<span class="comment">// Turn the input range into a UTF-32 range, so that we can be sure</span>
|
||
<span class="comment">// that we fall into the Unicode-aware parsing path inside parse()</span>
|
||
<span class="comment">// below.</span>
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">range</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">parser</span><span class="special">::</span><span class="identifier">as_utf32</span><span class="special">(</span><span class="identifier">str</span><span class="special">);</span>
|
||
<span class="keyword">using</span> <span class="identifier">iter_t</span> <span class="special">=</span> <span class="keyword">decltype</span><span class="special">(</span><span class="identifier">range</span><span class="special">.</span><span class="identifier">begin</span><span class="special">());</span>
|
||
|
||
<span class="keyword">if</span> <span class="special">(</span><span class="identifier">max_recursion</span> <span class="special"><=</span> <span class="number">0</span><span class="special">)</span>
|
||
<span class="identifier">max_recursion</span> <span class="special">=</span> <span class="identifier">INT_MAX</span><span class="special">;</span>
|
||
|
||
<span class="comment">// Initialize our globals to the current depth (0), and the max depth</span>
|
||
<span class="comment">// (max_recursion).</span>
|
||
<span class="identifier">global_state</span> <span class="identifier">globals</span><span class="special">{</span><span class="number">0</span><span class="special">,</span> <span class="identifier">max_recursion</span><span class="special">};</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">callback_error_handler</span> <span class="identifier">error_handler</span><span class="special">(</span><span class="identifier">errors_callback</span><span class="special">);</span>
|
||
<span class="comment">// Make a new parser that includes the globals and error handler.</span>
|
||
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">parser</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">with_error_handler</span><span class="special">(</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">with_globals</span><span class="special">(</span><span class="identifier">value_p</span><span class="special">,</span> <span class="identifier">globals</span><span class="special">),</span> <span class="identifier">error_handler</span><span class="special">);</span>
|
||
|
||
<span class="keyword">try</span> <span class="special">{</span>
|
||
<span class="comment">// Parse. If no exception is thrown, due to: a failed expectation</span>
|
||
<span class="comment">// (such as foo > bar, where foo matches the input, but then bar</span>
|
||
<span class="comment">// cannot); or because the nesting depth is exceeded; we simply</span>
|
||
<span class="comment">// return the result of the parse. The result will contextually</span>
|
||
<span class="comment">// convert to false if the parse failed. Note that the</span>
|
||
<span class="comment">// failed-expectation exception is caught internally, and used to</span>
|
||
<span class="comment">// generate an error message.</span>
|
||
<span class="keyword">return</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">parse</span><span class="special">(</span><span class="identifier">range</span><span class="special">,</span> <span class="identifier">parser</span><span class="special">,</span> <span class="identifier">ws</span><span class="special">);</span>
|
||
<span class="special">}</span> <span class="keyword">catch</span> <span class="special">(</span><span class="identifier">excessive_nesting</span><span class="special"><</span><span class="identifier">iter_t</span><span class="special">></span> <span class="keyword">const</span> <span class="special">&</span> <span class="identifier">e</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="comment">// If we catch an excessive_nesting exception, just report it</span>
|
||
<span class="comment">// and return an empty/failure result.</span>
|
||
<span class="keyword">if</span> <span class="special">(</span><span class="identifier">errors_callback</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="keyword">const</span> <span class="identifier">message</span> <span class="special">=</span> <span class="string">"error: Exceeded maximum number ("</span> <span class="special">+</span>
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">to_string</span><span class="special">(</span><span class="identifier">max_recursion</span><span class="special">)</span> <span class="special">+</span>
|
||
<span class="string">") of open arrays and/or objects"</span><span class="special">;</span>
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">stringstream</span> <span class="identifier">ss</span><span class="special">;</span>
|
||
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">write_formatted_message</span><span class="special">(</span>
|
||
<span class="identifier">ss</span><span class="special">,</span> <span class="string">""</span><span class="special">,</span> <span class="identifier">range</span><span class="special">.</span><span class="identifier">begin</span><span class="special">(),</span> <span class="identifier">e</span><span class="special">.</span><span class="identifier">iter</span><span class="special">,</span> <span class="identifier">range</span><span class="special">.</span><span class="identifier">end</span><span class="special">(),</span> <span class="identifier">message</span><span class="special">);</span>
|
||
<span class="identifier">errors_callback</span><span class="special">(</span><span class="identifier">ss</span><span class="special">.</span><span class="identifier">str</span><span class="special">());</span>
|
||
<span class="special">}</span>
|
||
<span class="special">}</span>
|
||
|
||
<span class="keyword">return</span> <span class="special">{};</span>
|
||
<span class="special">}</span>
|
||
|
||
<span class="special">}</span>
|
||
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">file_slurp</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">ifstream</span> <span class="special">&</span> <span class="identifier">ifs</span><span class="special">)</span>
|
||
<span class="special">{</span>
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">retval</span><span class="special">;</span>
|
||
<span class="keyword">while</span> <span class="special">(</span><span class="identifier">ifs</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="keyword">char</span> <span class="keyword">const</span> <span class="identifier">c</span> <span class="special">=</span> <span class="identifier">ifs</span><span class="special">.</span><span class="identifier">get</span><span class="special">();</span>
|
||
<span class="identifier">retval</span> <span class="special">+=</span> <span class="identifier">c</span><span class="special">;</span>
|
||
<span class="special">}</span>
|
||
<span class="keyword">if</span> <span class="special">(!</span><span class="identifier">retval</span><span class="special">.</span><span class="identifier">empty</span><span class="special">()</span> <span class="special">&&</span> <span class="identifier">retval</span><span class="special">.</span><span class="identifier">back</span><span class="special">()</span> <span class="special">==</span> <span class="special">-</span><span class="number">1</span><span class="special">)</span>
|
||
<span class="identifier">retval</span><span class="special">.</span><span class="identifier">pop_back</span><span class="special">();</span>
|
||
<span class="keyword">return</span> <span class="identifier">retval</span><span class="special">;</span>
|
||
<span class="special">}</span>
|
||
|
||
<span class="keyword">int</span> <span class="identifier">main</span><span class="special">(</span><span class="keyword">int</span> <span class="identifier">argc</span><span class="special">,</span> <span class="keyword">char</span> <span class="special">*</span> <span class="identifier">argv</span><span class="special">[])</span>
|
||
<span class="special">{</span>
|
||
<span class="keyword">if</span> <span class="special">(</span><span class="identifier">argc</span> <span class="special"><</span> <span class="number">2</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">cerr</span> <span class="special"><<</span> <span class="string">"A filename to parse is required.\n"</span><span class="special">;</span>
|
||
<span class="identifier">exit</span><span class="special">(</span><span class="number">1</span><span class="special">);</span>
|
||
<span class="special">}</span>
|
||
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">ifstream</span> <span class="identifier">ifs</span><span class="special">(</span><span class="identifier">argv</span><span class="special">[</span><span class="number">1</span><span class="special">]);</span>
|
||
<span class="keyword">if</span> <span class="special">(!</span><span class="identifier">ifs</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">cerr</span> <span class="special"><<</span> <span class="string">"Unable to read file '"</span> <span class="special"><<</span> <span class="identifier">argv</span><span class="special">[</span><span class="number">1</span><span class="special">]</span> <span class="special"><<</span> <span class="string">"'.\n"</span><span class="special">;</span>
|
||
<span class="identifier">exit</span><span class="special">(</span><span class="number">1</span><span class="special">);</span>
|
||
<span class="special">}</span>
|
||
|
||
<span class="comment">// Read in the entire file.</span>
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="keyword">const</span> <span class="identifier">file_contents</span> <span class="special">=</span> <span class="identifier">file_slurp</span><span class="special">(</span><span class="identifier">ifs</span><span class="special">);</span>
|
||
<span class="comment">// Parse the contents. If there is an error, just stream it to cerr.</span>
|
||
<span class="keyword">auto</span> <span class="identifier">json</span> <span class="special">=</span> <span class="identifier">json</span><span class="special">::</span><span class="identifier">parse</span><span class="special">(</span>
|
||
<span class="identifier">file_contents</span><span class="special">,</span> <span class="special">[](</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="keyword">const</span> <span class="special">&</span> <span class="identifier">msg</span><span class="special">)</span> <span class="special">{</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">cerr</span> <span class="special"><<</span> <span class="identifier">msg</span><span class="special">;</span> <span class="special">});</span>
|
||
<span class="keyword">if</span> <span class="special">(!</span><span class="identifier">json</span><span class="special">)</span> <span class="special">{</span>
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">cerr</span> <span class="special"><<</span> <span class="string">"Parse failure.\n"</span><span class="special">;</span>
|
||
<span class="identifier">exit</span><span class="special">(</span><span class="number">1</span><span class="special">);</span>
|
||
<span class="special">}</span>
|
||
|
||
<span class="identifier">std</span><span class="special">::</span><span class="identifier">cout</span> <span class="special"><<</span> <span class="string">"Parse successful; contents:\n"</span> <span class="special"><<</span> <span class="special">*</span><span class="identifier">json</span> <span class="special"><<</span> <span class="string">"\n"</span><span class="special">;</span>
|
||
|
||
<span class="keyword">return</span> <span class="number">0</span><span class="special">;</span>
|
||
<span class="special">}</span>
|
||
</pre>
|
||
<p>
|
||
</p>
|
||
</div>
|
||
<div class="copyright-footer">Copyright © 2020 T. Zachary Laine<p>
|
||
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||
file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
|
||
</p>
|
||
</div>
|
||
<hr>
|
||
<div class="spirit-nav">
|
||
<a accesskey="p" href="../extended_examples.html"><img src="../../images/prev.png" alt="Prev"></a><a accesskey="u" href="../extended_examples.html"><img src="../../images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../images/home.png" alt="Home"></a><a accesskey="n" href="parsing_json_with_callbacks.html"><img src="../../images/next.png" alt="Next"></a>
|
||
</div>
|
||
</body>
|
||
</html>
|