655 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
			
		
		
	
	
			655 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
/*  $Id$
 | 
						|
 | 
						|
    Part of SWI-Prolog
 | 
						|
 | 
						|
    Author:        Jan Wielemaker
 | 
						|
    E-mail:        J.Wielemaker@uva.nl
 | 
						|
    WWW:           http://www.swi-prolog.org
 | 
						|
    Copyright (C): 1985-2009, University of Amsterdam
 | 
						|
 | 
						|
    This program is free software; you can redistribute it and/or
 | 
						|
    modify it under the terms of the GNU General Public License
 | 
						|
    as published by the Free Software Foundation; either version 2
 | 
						|
    of the License, or (at your option) any later version.
 | 
						|
 | 
						|
    This program is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
    GNU General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU General Public
 | 
						|
    License along with this library; if not, write to the Free Software
 | 
						|
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
						|
 | 
						|
    As a special exception, if you link this library with other files,
 | 
						|
    compiled with a Free Software compiler, to produce an executable, this
 | 
						|
    library does not by itself cause the resulting executable to be covered
 | 
						|
    by the GNU General Public License. This exception does not however
 | 
						|
    invalidate any other reasons why the executable file might be covered by
 | 
						|
    the GNU General Public License.
 | 
						|
*/
 | 
						|
 | 
						|
:- module(json,
 | 
						|
	  [ json_read/2,		% +Stream, -JSONTerm
 | 
						|
	    json_read/3,		% +Stream, -JSONTerm, +Options
 | 
						|
	    atom_json_term/3,		% ?Atom, ?JSONTerm, +Options
 | 
						|
	    json_write/2,		% +Stream, +Term
 | 
						|
	    json_write/3,		% +Stream, +Term, +Options
 | 
						|
	    is_json_term/1,		% @Term
 | 
						|
	    is_json_term/2		% @Term, +Options
 | 
						|
	  ]).
 | 
						|
:- use_module(library(record)).
 | 
						|
:- use_module(library(memfile)).
 | 
						|
:- use_module(library(error)).
 | 
						|
:- use_module(library(option)).
 | 
						|
 | 
						|
:- use_foreign_library(foreign(json)).
 | 
						|
 | 
						|
/** <module> Reading and writing JSON serialization
 | 
						|
 | 
						|
This module supports reading and writing JSON objects. The canonical
 | 
						|
Prolog representation for a JSON value is defined as:
 | 
						|
 | 
						|
    * A JSON object is mapped to a term json(NameValueList), where
 | 
						|
    NameValueList is a list of Name=Value. Name is an atom created from
 | 
						|
    the JSON string.
 | 
						|
 | 
						|
    * A JSON array is mapped to a Prolog list of JSON values.
 | 
						|
 | 
						|
    * A JSON string is mapped to a Prolog atom
 | 
						|
 | 
						|
    * A JSON number is mapped to a Prolog number
 | 
						|
 | 
						|
    * The JSON constants =true= and =false= are mapped -like JPL- to
 | 
						|
    @(true) and @(false).
 | 
						|
 | 
						|
    * The JSON constant =null= is mapped to the Prolog term @(null)
 | 
						|
 | 
						|
Here is a complete example in JSON and its corresponding Prolog term.
 | 
						|
 | 
						|
==
 | 
						|
{ "name":"Demo term",
 | 
						|
  "created": {
 | 
						|
    "day":null,
 | 
						|
    "month":"December",
 | 
						|
    "year":2007
 | 
						|
  },
 | 
						|
  "confirmed":true,
 | 
						|
  "members":[1,2,3]
 | 
						|
}
 | 
						|
==
 | 
						|
 | 
						|
==
 | 
						|
json([ name='Demo term',
 | 
						|
       created=json([day= @null, month='December', year=2007]),
 | 
						|
       confirmed= @true,
 | 
						|
       members=[1, 2, 3]
 | 
						|
     ])
 | 
						|
==
 | 
						|
 | 
						|
@author Jan Wielemaker
 | 
						|
@see	http_json.pl links JSON to the HTTP client and server modules.
 | 
						|
@see	json_convert.pl converts JSON Prolog terms to more comfortable
 | 
						|
terms.
 | 
						|
*/
 | 
						|
 | 
						|
:- record json_options(null:ground = @(null),
 | 
						|
		       true:ground = @(true),
 | 
						|
		       false:ground = @(false),
 | 
						|
		       value_string_as:oneof([atom,string]) = atom).
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	       MAP		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
%%	atom_json_term(+Atom, -JSONTerm, +Options) is det.
 | 
						|
%%	atom_json_term(-Text, +JSONTerm, +Options) is det.
 | 
						|
%
 | 
						|
%	Convert between textual  representation  and   a  JSON  term. In
 | 
						|
%	_write_ mode, the option as(Type) defines the output type, which
 | 
						|
%	is one of =atom=, =string= or =codes=.
 | 
						|
 | 
						|
atom_json_term(Atom, Term, Options) :-
 | 
						|
	ground(Atom), !,
 | 
						|
	atom_to_memory_file(Atom, MF),
 | 
						|
	open_memory_file(MF, read, In),
 | 
						|
	call_cleanup(json_read(In, Term, Options),
 | 
						|
		     (	 close(In),
 | 
						|
			 free_memory_file(MF))).
 | 
						|
atom_json_term(Result, Term, Options) :-
 | 
						|
	select_option(as(Type), Options, Options1),
 | 
						|
	(   type_term(Type, Result, Out)
 | 
						|
	->  true
 | 
						|
	;   must_be(oneof([atom,string,codes]), Type)
 | 
						|
	),
 | 
						|
	with_output_to(Out,
 | 
						|
		       json_write(current_output, Term, Options1)).
 | 
						|
 | 
						|
type_term(atom,   Result, atom(Result)).
 | 
						|
type_term(string, Result, string(Result)).
 | 
						|
type_term(codes,  Result, codes(Result)).
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	     READING		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
%%	json_read(+Stream, -Term) is det.
 | 
						|
%%	json_read(+Stream, -Term, +Options) is det.
 | 
						|
%
 | 
						|
%	Read next JSON value from Stream into a Prolog term. Options
 | 
						|
%	are:
 | 
						|
%
 | 
						|
%		* null(NullTerm)
 | 
						|
%		Term used to represent JSON =null=.  Default @(null)
 | 
						|
%		* true(TrueTerm)
 | 
						|
%		Term used to represent JSON =true=.  Default @(true)
 | 
						|
%		* false(FalsTerm)
 | 
						|
%		Term used to represent JSON =false=.  Default @(false)
 | 
						|
%		* value_string_as(Type)
 | 
						|
%		Prolog type used for strings used as value.  Default
 | 
						|
%		is =atom=.  The alternative is =string=, producing a
 | 
						|
%		packed string object.  Please note that =codes= or
 | 
						|
%		=chars= would produce ambiguous output and is therefore
 | 
						|
%		not supported.
 | 
						|
 | 
						|
json_read(Stream, Term) :-
 | 
						|
	default_json_options(Options),
 | 
						|
	json_value(Stream, Term, _, Options).
 | 
						|
 | 
						|
json_read(Stream, Term, Options) :-
 | 
						|
	make_json_options(Options, OptionTerm, _RestOptions),
 | 
						|
	json_value(Stream, Term, _, OptionTerm).
 | 
						|
 | 
						|
 | 
						|
json_value(Stream, Term, Next, Options) :-
 | 
						|
	get_code(Stream, C0),
 | 
						|
	ws(C0, Stream, C1),
 | 
						|
	json_term(C1, Stream, Term, Next, Options).
 | 
						|
 | 
						|
json_term(0'{, Stream, json(Pairs), Next, Options) :- !,
 | 
						|
	ws(Stream, C),
 | 
						|
	json_pairs(C, Stream, Pairs, Options),
 | 
						|
	get_code(Stream, Next).
 | 
						|
json_term(0'[, Stream, Array, Next, Options) :- !,
 | 
						|
	ws(Stream, C),
 | 
						|
	json_array(C, Stream, Array, Options),
 | 
						|
	get_code(Stream, Next).
 | 
						|
json_term(0'", Stream, String, Next, Options) :- !,
 | 
						|
	get_code(Stream, C1),
 | 
						|
	json_string_codes(C1, Stream, Codes),
 | 
						|
	json_options_value_string_as(Options, Type),
 | 
						|
	codes_to_type(Type, Codes, String),
 | 
						|
	get_code(Stream, Next).
 | 
						|
json_term(0'-, Stream, Number, Next, _Options) :- !,
 | 
						|
	json_number_codes(Stream, Codes, Next),
 | 
						|
	number_codes(Number, [0'-|Codes]).
 | 
						|
json_term(D, Stream, Number, Next, _Options) :-
 | 
						|
	between(0'0, 0'9, D), !,
 | 
						|
	json_number_codes(Stream, Codes, Next),
 | 
						|
	number_codes(Number, [D|Codes]).
 | 
						|
json_term(C, Stream, Constant, Next, Options) :-
 | 
						|
	get_code(Stream, C1),
 | 
						|
	json_identifier_codes(C1, Stream, Codes, Next),
 | 
						|
	atom_codes(ID, [C|Codes]),
 | 
						|
	json_constant(ID, Constant, Options).
 | 
						|
 | 
						|
json_pairs(0'}, _, [], _) :- !.
 | 
						|
json_pairs(C0, Stream, [Pair|Tail], Options) :-
 | 
						|
	json_pair(C0, Stream, Pair, C, Options),
 | 
						|
	ws(C, Stream, Next),
 | 
						|
	(   Next == 0',
 | 
						|
	->  ws(Stream, C2),
 | 
						|
	    json_pairs(C2, Stream, Tail, Options)
 | 
						|
	;   Next == 0'}
 | 
						|
	->  Tail = []
 | 
						|
	;   syntax_error(illegal_object, Stream)
 | 
						|
	).
 | 
						|
 | 
						|
json_pair(C0, Stream, Name=Value, Next, Options) :-
 | 
						|
	json_string_as_atom(C0, Stream, Name),
 | 
						|
	ws(Stream, C),
 | 
						|
	C == 0':,
 | 
						|
	json_value(Stream, Value, Next, Options).
 | 
						|
 | 
						|
 | 
						|
json_array(0'], _, [], _) :- !.
 | 
						|
json_array(C0, Stream, [Value|Tail], Options) :-
 | 
						|
	json_term(C0, Stream, Value, C, Options),
 | 
						|
	ws(C, Stream, Next),
 | 
						|
	(   Next == 0',
 | 
						|
	->  ws(Stream, C1),
 | 
						|
	    json_array(C1, Stream, Tail, Options)
 | 
						|
	;   Next == 0']
 | 
						|
	->  Tail = []
 | 
						|
	).
 | 
						|
 | 
						|
codes_to_type(atom, Codes, Atom) :-
 | 
						|
	atom_codes(Atom, Codes).
 | 
						|
codes_to_type(string, Codes, Atom) :-
 | 
						|
	string_to_list(Atom, Codes).
 | 
						|
codes_to_type(codes, Codes, Codes).
 | 
						|
 | 
						|
json_string_as_atom(0'", Stream, Atom) :-
 | 
						|
	get_code(Stream, C1),
 | 
						|
	json_string_codes(C1, Stream, Codes),
 | 
						|
	atom_codes(Atom, Codes).
 | 
						|
 | 
						|
json_string_codes(0'", _, []) :- !.
 | 
						|
json_string_codes(0'\\, Stream, [H|T]) :- !,
 | 
						|
	get_code(Stream, C0),
 | 
						|
	(   escape(C0, Stream, H)
 | 
						|
	->  true
 | 
						|
	;   syntax_error(illegal_string_escape, Stream)
 | 
						|
	),
 | 
						|
	get_code(Stream, C1),
 | 
						|
	json_string_codes(C1, Stream, T).
 | 
						|
json_string_codes(C, Stream, [C|T]) :-
 | 
						|
	get_code(Stream, C1),
 | 
						|
	json_string_codes(C1, Stream, T).
 | 
						|
 | 
						|
escape(0'", _, 0'") :- !.
 | 
						|
escape(0'\\, _, 0'\\) :- !.
 | 
						|
escape(0'/, _, 0'/) :- !.
 | 
						|
escape(0'b, _, 0'\b) :- !.
 | 
						|
escape(0'f, _, 0'\f) :- !.
 | 
						|
escape(0'n, _, 0'\n) :- !.
 | 
						|
escape(0'r, _, 0'\r) :- !.
 | 
						|
escape(0't, _, 0'\t) :- !.
 | 
						|
escape(0'u, Stream, C) :- !,
 | 
						|
	get_code(Stream, C1),
 | 
						|
	get_code(Stream, C2),
 | 
						|
	get_code(Stream, C3),
 | 
						|
	get_code(Stream, C4),
 | 
						|
	code_type(C1, xdigit(D1)),
 | 
						|
	code_type(C2, xdigit(D2)),
 | 
						|
	code_type(C3, xdigit(D3)),
 | 
						|
	code_type(C4, xdigit(D4)),
 | 
						|
	C is D1<<12+D2<<8+D3<<4+D4.
 | 
						|
 | 
						|
json_number_codes(Stream, Codes, Next) :-
 | 
						|
	get_code(Stream, C1),
 | 
						|
	json_number_codes(C1, Stream, Codes, Next).
 | 
						|
 | 
						|
json_number_codes(C1, Stream, [C1|Codes], Next) :-
 | 
						|
	number_code(C1), !,
 | 
						|
	get_code(Stream, C2),
 | 
						|
	json_number_codes(C2, Stream, Codes, Next).
 | 
						|
json_number_codes(C, _, [], C).
 | 
						|
 | 
						|
number_code(C) :-
 | 
						|
	between(0'0, 0'9, C), !.
 | 
						|
number_code(0'.).
 | 
						|
number_code(0'-).
 | 
						|
number_code(0'e).
 | 
						|
number_code(0'E).
 | 
						|
 | 
						|
json_identifier_codes(C1, Stream, [C1|T], Next) :-
 | 
						|
	between(0'a, 0'z, C1), !,
 | 
						|
	get_code(Stream, C2),
 | 
						|
	json_identifier_codes(C2, Stream, T, Next).
 | 
						|
json_identifier_codes(C, _, [], C).
 | 
						|
 | 
						|
 | 
						|
json_constant(true, Constant, Options) :- !,
 | 
						|
	json_options_true(Options, Constant).
 | 
						|
json_constant(false, Constant, Options) :- !,
 | 
						|
	json_options_false(Options, Constant).
 | 
						|
json_constant(null, Constant, Options) :- !,
 | 
						|
	json_options_null(Options, Constant).
 | 
						|
 | 
						|
%%	ws(+Stream, -Next) is det.
 | 
						|
%%	ws(+C0, +Stream, -Next)
 | 
						|
%
 | 
						|
%	Skip white space on the Stream, returning the first non-ws
 | 
						|
%	character.  Also skips =|//|= ... comments.
 | 
						|
 | 
						|
ws(Stream, Next) :-
 | 
						|
	get_code(Stream, C0),
 | 
						|
	ws(C0, Stream, Next).
 | 
						|
 | 
						|
ws(C0, Stream, C) :-
 | 
						|
	ws(C0), !,
 | 
						|
	get_code(Stream, C1),
 | 
						|
	ws(C1, Stream, C).
 | 
						|
ws(0'/, Stream, C) :- !,
 | 
						|
	get_code(Stream, Cmt1), !,
 | 
						|
	expect(Cmt1, 0'/, Stream),
 | 
						|
	skip(Stream, 0'\n),
 | 
						|
	get_code(Stream, C).
 | 
						|
ws(C, _, C).
 | 
						|
 | 
						|
ws(0' ).
 | 
						|
ws(0'\t).
 | 
						|
ws(0'\n).
 | 
						|
ws(0'\r).
 | 
						|
 | 
						|
expect(C, C, _) :- !.
 | 
						|
expect(_, 0'/, Stream) :- !,
 | 
						|
	syntax_error(illegal_comment, Stream).
 | 
						|
 | 
						|
syntax_error(Message, Stream) :-
 | 
						|
	stream_error_context(Stream, Context),
 | 
						|
	throw(error(syntax_error(json(Message)), Context)).
 | 
						|
 | 
						|
stream_error_context(Stream, stream(Stream, Line, LinePos, CharNo)) :-
 | 
						|
	character_count(Stream, CharNo),
 | 
						|
	line_position(Stream, LinePos),
 | 
						|
	line_count(Stream, Line).
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	    JSON OUTPUT		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
%%	json_write_string(+Stream, +Text) is det.
 | 
						|
%
 | 
						|
%	Write a JSON string to  Stream.  Stream   must  be  opened  in a
 | 
						|
%	Unicode capable encoding, typically UTF-8.
 | 
						|
 | 
						|
% foreign json_write_string/2.
 | 
						|
 | 
						|
%%	json_write_indent(+Stream, +Indent, +TabDistance) is det.
 | 
						|
%
 | 
						|
%	Newline and indent to  Indent.  A   Newline  is  only written if
 | 
						|
%	line_position(Stream, Pos) is not 0. Then   it  writes Indent //
 | 
						|
%	TabDistance tab characters and Indent mode TabDistance spaces.
 | 
						|
 | 
						|
% foreign json_write_indent/3.
 | 
						|
 | 
						|
%%	json_write(+Stream, +Term) is det.
 | 
						|
%%	json_write(+Stream, +Term, +Options) is det.
 | 
						|
%
 | 
						|
%	Write a JSON term to Stream.  The   JSON  object  is of the same
 | 
						|
%	format as produced by json_read/2, though we allow for some more
 | 
						|
%	flexibility with regard to pairs in  objects. All of Name=Value,
 | 
						|
%	Name-Value and Name(Value) produce the  same output. In addition
 | 
						|
%	to  the  options  recognised  by  json_read/3,  we  process  the
 | 
						|
%	following options are recognised:
 | 
						|
%
 | 
						|
%	    * width(+Width)
 | 
						|
%	    Width in which we try to format the result.  Too long lines
 | 
						|
%	    switch from _horizontal_ to _vertical_ layout for better
 | 
						|
%	    readability. If performance is critical and human
 | 
						|
%	    readability is not an issue use Width = 0, which causes a
 | 
						|
%	    single-line output.
 | 
						|
%
 | 
						|
%	    * step(+Step)
 | 
						|
%	    Indentation increnment for next level.  Default is 2.
 | 
						|
%
 | 
						|
%	    * tab(+TabDistance)
 | 
						|
%	    Distance between tab-stops.  If equal to Step, layout
 | 
						|
%	    is generated with one tab per level.
 | 
						|
 | 
						|
:- record json_write_state(indent:nonneg = 0,
 | 
						|
			   step:positive_integer = 2,
 | 
						|
			   tab:positive_integer = 8,
 | 
						|
			   width:nonneg = 72).
 | 
						|
 | 
						|
json_write(Stream, Term) :-
 | 
						|
	json_write(Stream, Term, []).
 | 
						|
json_write(Stream, Term, Options) :-
 | 
						|
	make_json_write_state(Options, State, Options1),
 | 
						|
	make_json_options(Options1, OptionTerm, _RestOptions),
 | 
						|
	json_write_term(Term, Stream, State, OptionTerm).
 | 
						|
 | 
						|
json_write_term(Var, _, _, _) :-
 | 
						|
	var(Var), !,
 | 
						|
	instantiation_error(Var).
 | 
						|
json_write_term(json(Pairs), Stream, State, Options) :- !,
 | 
						|
	space_if_not_at_left_margin(Stream),
 | 
						|
	write(Stream, '{'),
 | 
						|
	(   json_write_state_width(State, Width),
 | 
						|
	    (   Width == 0
 | 
						|
	    ->  true
 | 
						|
	    ;   json_write_state_indent(State, Indent),
 | 
						|
		json_print_length(json(Pairs), Options, Width, Indent, _)
 | 
						|
	    )
 | 
						|
	->  set_width_of_json_write_state(0, State, State2),
 | 
						|
	    write_pairs_hor(Pairs, Stream, State2, Options),
 | 
						|
	    write(Stream, '}')
 | 
						|
	;   step_indent(State, State2),
 | 
						|
	    write_pairs_ver(Pairs, Stream, State2, Options),
 | 
						|
	    indent(Stream, State),
 | 
						|
	    write(Stream, '}')
 | 
						|
	).
 | 
						|
json_write_term(List, Stream, State, Options) :-
 | 
						|
	is_list(List), !,
 | 
						|
	space_if_not_at_left_margin(Stream),
 | 
						|
	write(Stream, '['),
 | 
						|
	(   json_write_state_width(State, Width),
 | 
						|
	    (   Width == 0
 | 
						|
	    ->  true
 | 
						|
	    ;   json_write_state_indent(State, Indent),
 | 
						|
		json_print_length(List, Options, Width, Indent, _)
 | 
						|
	    )
 | 
						|
	->  set_width_of_json_write_state(0, State, State2),
 | 
						|
	    write_array_hor(List, Stream, State2, Options),
 | 
						|
	    write(Stream, ']')
 | 
						|
	;   step_indent(State, State2),
 | 
						|
	    write_array_ver(List, Stream, State2, Options),
 | 
						|
	    indent(Stream, State),
 | 
						|
	    write(Stream, ']')
 | 
						|
	).
 | 
						|
json_write_term(Number, Stream, _State, _Options) :-
 | 
						|
	number(Number), !,
 | 
						|
	write(Stream, Number).
 | 
						|
json_write_term(True, Stream, _State, Options) :-
 | 
						|
	json_options_true(Options, True), !,
 | 
						|
	write(Stream, true).
 | 
						|
json_write_term(False, Stream, _State, Options) :-
 | 
						|
	json_options_false(Options, False), !,
 | 
						|
	write(Stream, false).
 | 
						|
json_write_term(Null, Stream, _State, Options) :-
 | 
						|
	json_options_null(Options, Null), !,
 | 
						|
	write(Stream, null).
 | 
						|
json_write_term(String, Stream, _State, _Options) :-
 | 
						|
	json_write_string(Stream, String).
 | 
						|
 | 
						|
write_pairs_hor([], _, _, _).
 | 
						|
write_pairs_hor([H|T], Stream, State, Options) :-
 | 
						|
	json_pair(H, Name, Value),
 | 
						|
	json_write_string(Stream, Name),
 | 
						|
	write(Stream, ':'),
 | 
						|
	json_write_term(Value, Stream, State, Options),
 | 
						|
	(   T == []
 | 
						|
	->  true
 | 
						|
	;   write(Stream, ', '),
 | 
						|
	    write_pairs_hor(T, Stream, State, Options)
 | 
						|
	).
 | 
						|
 | 
						|
write_pairs_ver([], _, _, _).
 | 
						|
write_pairs_ver([H|T], Stream, State, Options) :-
 | 
						|
	indent(Stream, State),
 | 
						|
	json_pair(H, Name, Value),
 | 
						|
	json_write_string(Stream, Name),
 | 
						|
	write(Stream, ':'),
 | 
						|
	json_write_term(Value, Stream, State, Options),
 | 
						|
	(   T == []
 | 
						|
	->  true
 | 
						|
	;   write(Stream, ','),
 | 
						|
	    write_pairs_ver(T, Stream, State, Options)
 | 
						|
	).
 | 
						|
 | 
						|
 | 
						|
json_pair(Var, _, _) :-
 | 
						|
	var(Var), !,
 | 
						|
	instantiation_error(Var).
 | 
						|
json_pair(Name=Value, Name, Value) :- !.
 | 
						|
json_pair(Name-Value, Name, Value) :- !.
 | 
						|
json_pair(NameValue, Name, Value) :-
 | 
						|
	compound(NameValue),
 | 
						|
	NameValue =.. [Name, Value], !.
 | 
						|
json_pair(Pair, _, _) :-
 | 
						|
	type_error(json_pair, Pair).
 | 
						|
 | 
						|
 | 
						|
write_array_hor([], _, _, _).
 | 
						|
write_array_hor([H|T], Stream, State, Options) :-
 | 
						|
	json_write_term(H, Stream, State, Options),
 | 
						|
	(   T == []
 | 
						|
	->  true
 | 
						|
	;   write(Stream, ', '),
 | 
						|
	    write_array_hor(T, Stream, State, Options)
 | 
						|
	).
 | 
						|
 | 
						|
write_array_ver([], _, _, _).
 | 
						|
write_array_ver([H|T], Stream, State, Options) :-
 | 
						|
	indent(Stream, State),
 | 
						|
	json_write_term(H, Stream, State, Options),
 | 
						|
	(   T == []
 | 
						|
	->  true
 | 
						|
	;   write(Stream, ','),
 | 
						|
	    write_array_ver(T, Stream, State, Options)
 | 
						|
	).
 | 
						|
 | 
						|
 | 
						|
indent(Stream, State) :-
 | 
						|
	json_write_state_indent(State, Indent),
 | 
						|
	json_write_state_tab(State, Tab),
 | 
						|
	json_write_indent(Stream, Indent, Tab).
 | 
						|
 | 
						|
step_indent(State0, State) :-
 | 
						|
	json_write_state_indent(State0, Indent),
 | 
						|
	json_write_state_step(State0, Step),
 | 
						|
	NewIndent is Indent+Step,
 | 
						|
	set_indent_of_json_write_state(NewIndent, State0, State).
 | 
						|
 | 
						|
space_if_not_at_left_margin(Stream) :-
 | 
						|
	line_position(Stream, 0), !.
 | 
						|
space_if_not_at_left_margin(Stream) :-
 | 
						|
	put_char(Stream, ' ').
 | 
						|
 | 
						|
 | 
						|
%%	json_print_length(+Value, +Options, +Max, +Len0, +Len) is semidet.
 | 
						|
%
 | 
						|
%	True if Len-Len0 is the print-length of Value on a single line
 | 
						|
%	and Len-Len0 =< Max.
 | 
						|
%
 | 
						|
%	@tbd	Escape sequences in strings are not considered.
 | 
						|
 | 
						|
json_print_length(json(Pairs), Options, Max, Len0, Len) :- !,
 | 
						|
	Len1 is Len0 + 2,
 | 
						|
	Len1 =< Max,
 | 
						|
	pairs_print_length(Pairs, Options, Max, Len1, Len).
 | 
						|
json_print_length(Array, Options, Max, Len0, Len) :-
 | 
						|
	is_list(Array), !,
 | 
						|
	Len1 is Len0 + 2,
 | 
						|
	Len1 =< Max,
 | 
						|
	array_print_length(Array, Options, Max, Len1, Len).
 | 
						|
json_print_length(Null, Options, Max, Len0, Len) :-
 | 
						|
  json_options_null(Options, Null), !,
 | 
						|
	Len is Len0 + 4,
 | 
						|
	Len =< Max.
 | 
						|
json_print_length(False, Options, Max, Len0, Len) :-
 | 
						|
  json_options_false(Options, False), !,
 | 
						|
	Len is Len0 + 5,
 | 
						|
	Len =< Max.
 | 
						|
json_print_length(True, Options, Max, Len0, Len) :-
 | 
						|
  json_options_true(Options, True), !,
 | 
						|
	Len is Len0 + 4,
 | 
						|
	Len =< Max.
 | 
						|
json_print_length(Number, _Options, Max, Len0, Len) :-
 | 
						|
	number(Number), !,
 | 
						|
	atom_length(Number, AL),
 | 
						|
	Len is Len0 + AL,
 | 
						|
	Len =< Max.
 | 
						|
json_print_length(@(Id), _Options, Max, Len0, Len) :- !,
 | 
						|
	atom_length(Id, IdLen),
 | 
						|
	Len is Len0+IdLen,
 | 
						|
	Len =< Max.
 | 
						|
json_print_length(String, _Options, Max, Len0, Len) :-
 | 
						|
	string_len(String, Len0, Len),
 | 
						|
	Len =< Max.
 | 
						|
 | 
						|
pairs_print_length([], _, _, Len, Len).
 | 
						|
pairs_print_length([H|T], Options, Max, Len0, Len) :-
 | 
						|
	pair_len(H, Options, Max, Len0, Len1),
 | 
						|
	(   T == []
 | 
						|
	->  Len = Len1
 | 
						|
	;   Len2 is Len1 + 2,
 | 
						|
	    Len2 =< Max,
 | 
						|
	    pairs_print_length(T, Options, Max, Len2, Len)
 | 
						|
	).
 | 
						|
 | 
						|
pair_len(Name=Value, Options, Max, Len0, Len) :-
 | 
						|
	string_len(Name, Len0, Len1),
 | 
						|
	Len2 is Len1+2,
 | 
						|
	Len2 =< Max,
 | 
						|
	json_print_length(Value, Options, Max, Len2, Len).
 | 
						|
 | 
						|
array_print_length([], _, _, Len, Len).
 | 
						|
array_print_length([H|T], Options, Max, Len0, Len) :-
 | 
						|
	json_print_length(H, Options, Max, Len0, Len1),
 | 
						|
	(   T == []
 | 
						|
	->  Len = Len1
 | 
						|
	;   Len2 is Len1+2,
 | 
						|
	    Len2 =< Max,
 | 
						|
	    array_print_length(T, Options, Max, Len2, Len)
 | 
						|
	).
 | 
						|
 | 
						|
string_len(String, Len0, Len) :-
 | 
						|
	atom_length(String, AL),
 | 
						|
	Len is Len0 + AL + 2.
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	       TEST		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
%%	is_json_term(@Term) is semidet.
 | 
						|
%%	is_json_term(@Term, +Options) is semidet.
 | 
						|
%
 | 
						|
%	True if Term is  a  json  term.   Options  are  the  same as for
 | 
						|
%	json_read/2, defining the Prolog  representation   for  the JSON
 | 
						|
%	=true=, =false= and =null= constants.
 | 
						|
 | 
						|
is_json_term(Term) :-
 | 
						|
	default_json_options(Options),
 | 
						|
	is_json_term2(Options, Term).
 | 
						|
 | 
						|
is_json_term(Term, Options) :-
 | 
						|
	make_json_options(Options, OptionTerm, _RestOptions),
 | 
						|
	is_json_term2(OptionTerm, Term).
 | 
						|
 | 
						|
is_json_term2(_, Var) :-
 | 
						|
	var(Var), !, fail.
 | 
						|
is_json_term2(Options, json(Pairs)) :- !,
 | 
						|
	is_list(Pairs),
 | 
						|
	maplist(is_json_pair(Options), Pairs).
 | 
						|
is_json_term2(Options, List) :-
 | 
						|
	is_list(List), !,
 | 
						|
	maplist(is_json_term2(Options), List).
 | 
						|
is_json_term2(_, Primitive) :-
 | 
						|
	atomic(Primitive), !.		% atom, string or number
 | 
						|
is_json_term2(Options, True) :-
 | 
						|
	json_options_true(Options, True).
 | 
						|
is_json_term2(Options, False) :-
 | 
						|
	json_options_false(Options, False).
 | 
						|
is_json_term2(Options, Null) :-
 | 
						|
	json_options_null(Options, Null).
 | 
						|
 | 
						|
is_json_pair(_, Var) :-
 | 
						|
	var(Var), !, fail.
 | 
						|
is_json_pair(Options, Name=Value) :-
 | 
						|
	atom(Name),
 | 
						|
	is_json_term2(Options, Value).
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	     MESSAGES		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
:- multifile
 | 
						|
	prolog:error_message/3.
 | 
						|
 | 
						|
prolog:error_message(syntax_error(json(Id))) -->
 | 
						|
	[ 'JSON syntax error: ' ],
 | 
						|
	json_syntax_error(Id).
 | 
						|
 | 
						|
json_syntax_error(illegal_comment) -->
 | 
						|
	[ 'Illegal comment' ].
 | 
						|
json_syntax_error(illegal_string_escape) -->
 | 
						|
	[ 'Illegal escape sequence in string' ].
 |