1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2014-2020, University of Amsterdam 7 VU University Amsterdam 8 CWI, Amsterdam 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(prolog_pretty_print, 38 [ print_term/2 % +Term, +Options 39 ]). 40:- autoload(library(option), 41 [merge_options/3, select_option/3, select_option/4, 42 option/2, option/3]). 43 44/** <module> Pretty Print Prolog terms 45 46This module is a first start of what should become a full-featured 47pretty printer for Prolog terms with many options and parameters. 48Eventually, it should replace portray_clause/1 and various other 49special-purpose predicates. 50 51@tbd This is just a quicky. We need proper handling of portray/1, avoid 52printing very long terms multiple times, spacing (around operators), 53etc. 54 55@tbd Use a record for the option-processing. 56 57@tbd The current approach is far too simple, often resulting in illegal 58 terms. 59*/ 60 61:- predicate_options(print_term/2, 2, 62 [ output(stream), 63 right_margin(integer), 64 left_margin(integer), 65 tab_width(integer), 66 indent_arguments(integer), 67 operators(boolean), 68 write_options(list) 69 ]). 70 71%! print_term(+Term, +Options) is det. 72% 73% Pretty print a Prolog term. The following options are processed: 74% 75% * output(+Stream) 76% Define the output stream. Default is =user_output= 77% * right_margin(+Integer) 78% Width of a line. Default is 72 characters. 79% * left_margin(+Integer) 80% Left margin for continuation lines. Default is 0. 81% * tab_width(+Integer) 82% Distance between tab-stops. Default is 8 characters. 83% * indent_arguments(+Spec) 84% Defines how arguments of compound terms are placed. Defined 85% values are: 86% $ =false= : 87% Simply place them left to right (no line-breaks) 88% $ =true= : 89% Place them vertically, aligned with the open bracket (not 90% implemented) 91% $ =auto= (default) : 92% As horizontal if line-width is not exceeded, vertical 93% otherwise. 94% $ An integer : 95% Place them vertically aligned, <N> spaces to the right of 96% the beginning of the head. 97% * operators(+Boolean) 98% This is the inverse of the write_term/3 option =ignore_ops=. 99% Default is to respect them. 100% * write_options(+List) 101% List of options passed to write_term/3 for terms that are 102% not further processed. Default: 103% == 104% [ numbervars(true), 105% quoted(true), 106% portray(true) 107% ] 108% == 109 110print_term(Term, Options) :- 111 \+ \+ print_term_2(Term, Options). 112 113print_term_2(Term, Options0) :- 114 prepare_term(Term, Template, Cycles, Constraints), 115 defaults(Defs0), 116 select_option(write_options(WrtDefs), Defs0, Defs), 117 select_option(write_options(WrtUser), Options0, Options1, []), 118 merge_options(WrtUser, WrtDefs, WrtOpts), 119 merge_options(Options1, Defs, Options2), 120 option(max_depth(MaxDepth), WrtOpts, infinite), 121 Options = [write_options(WrtOpts)|Options2], 122 123 dict_create(Context, #, [max_depth(MaxDepth)|Options]), 124 pp(Template, Context, Options), 125 print_extra(Cycles, Context, 'where', Options), 126 print_extra(Constraints, Context, 'with constraints', Options). 127 128print_extra([], _, _, _) :- !. 129print_extra(List, Context, Comment, Options) :- 130 option(output(Out), Options), 131 format(Out, ', % ~w', [Comment]), 132 modify_context(Context, [indent=4], Context1), 133 print_extra_2(List, Context1, Options). 134 135print_extra_2([H|T], Context, Options) :- 136 option(output(Out), Options), 137 context(Context, indent, Indent), 138 indent(Out, Indent, Options), 139 pp(H, Context, Options), 140 ( T == [] 141 -> true 142 ; format(Out, ',', []), 143 print_extra_2(T, Context, Options) 144 ). 145 146 147%! prepare_term(+Term, -Template, -Cycles, -Constraints) 148% 149% Prepare a term, possibly holding cycles and constraints for 150% printing. 151 152prepare_term(Term, Template, Cycles, Constraints) :- 153 term_attvars(Term, []), 154 !, 155 Constraints = [], 156 '$factorize_term'(Term, Template, Factors), 157 bind_non_cycles(Factors, 1, Cycles), 158 numbervars(Template+Cycles+Constraints, 0, _, 159 [singletons(true)]). 160prepare_term(Term, Template, Cycles, Constraints) :- 161 copy_term(Term, Copy, Constraints), 162 !, 163 '$factorize_term'(Copy, Template, Factors), 164 bind_non_cycles(Factors, 1, Cycles), 165 numbervars(Template+Cycles+Constraints, 0, _, 166 [singletons(true)]). 167 168 169bind_non_cycles([], _, []). 170bind_non_cycles([V=Term|T], I, L) :- 171 unify_with_occurs_check(V, Term), 172 !, 173 bind_non_cycles(T, I, L). 174bind_non_cycles([H|T0], I, [H|T]) :- 175 H = ('$VAR'(Name)=_), 176 atom_concat('_S', I, Name), 177 I2 is I + 1, 178 bind_non_cycles(T0, I2, T). 179 180 181defaults([ output(user_output), 182 left_margin(0), 183 right_margin(72), 184 depth(0), 185 indent(0), 186 indent_arguments(auto), 187 operators(true), 188 write_options([ quoted(true), 189 numbervars(true), 190 portray(true), 191 attributes(portray) 192 ]), 193 priority(1200) 194 ]). 195 196 197 /******************************* 198 * CONTEXT * 199 *******************************/ 200 201context(Ctx, Name, Value) :- 202 get_dict(Name, Ctx, Value). 203 204modify_context(Ctx0, Mapping, Ctx) :- 205 Ctx = Ctx0.put(Mapping). 206 207dec_depth(Ctx, Ctx) :- 208 context(Ctx, max_depth, infinite), 209 !. 210dec_depth(Ctx0, Ctx) :- 211 ND is Ctx0.max_depth - 1, 212 Ctx = Ctx0.put(max_depth, ND). 213 214 215 /******************************* 216 * PP * 217 *******************************/ 218 219pp(Primitive, Ctx, Options) :- 220 ( atomic(Primitive) 221 ; var(Primitive) 222 ; Primitive = '$VAR'(Var), 223 ( integer(Var) 224 ; atom(Var) 225 ) 226 ), 227 !, 228 pprint(Primitive, Ctx, Options). 229pp(Portray, _Ctx, Options) :- 230 option(write_options(WriteOptions), Options), 231 option(portray(true), WriteOptions), 232 option(output(Out), Options), 233 with_output_to(Out, user:portray(Portray)), 234 !. 235pp(List, Ctx, Options) :- 236 List = [_|_], 237 !, 238 context(Ctx, indent, Indent), 239 context(Ctx, depth, Depth), 240 option(output(Out), Options), 241 option(indent_arguments(IndentStyle), Options), 242 ( ( IndentStyle == false 243 -> true 244 ; IndentStyle == auto, 245 print_width(List, Width, Options), 246 option(right_margin(RM), Options), 247 Indent + Width < RM 248 ) 249 -> pprint(List, Ctx, Options) 250 ; format(Out, '[ ', []), 251 Nindent is Indent + 2, 252 NDepth is Depth + 1, 253 modify_context(Ctx, [indent=Nindent, depth=NDepth], NCtx), 254 pp_list_elements(List, NCtx, Options), 255 indent(Out, Indent, Options), 256 format(Out, ']', []) 257 ). 258:- if(current_predicate(is_dict/1)). 259pp(Dict, Ctx, Options) :- 260 is_dict(Dict), 261 !, 262 dict_pairs(Dict, Tag, Pairs), 263 option(output(Out), Options), 264 option(indent_arguments(IndentStyle), Options), 265 context(Ctx, indent, Indent), 266 ( IndentStyle == false ; Pairs == [] 267 -> pprint(Dict, Ctx, Options) 268 ; IndentStyle == auto, 269 print_width(Dict, Width, Options), 270 option(right_margin(RM), Options), 271 Indent + Width < RM % fits on a line, simply write 272 -> pprint(Dict, Ctx, Options) 273 ; format(atom(Buf2), '~q{ ', [Tag]), 274 write(Out, Buf2), 275 atom_length(Buf2, FunctorIndent), 276 ( integer(IndentStyle) 277 -> Nindent is Indent + IndentStyle, 278 ( FunctorIndent > IndentStyle 279 -> indent(Out, Nindent, Options) 280 ; true 281 ) 282 ; Nindent is Indent + FunctorIndent 283 ), 284 context(Ctx, depth, Depth), 285 NDepth is Depth + 1, 286 modify_context(Ctx, [indent=Nindent, depth=NDepth], NCtx0), 287 dec_depth(NCtx0, NCtx), 288 pp_dict_args(Pairs, NCtx, Options), 289 BraceIndent is Nindent - 2, % '{ ' 290 indent(Out, BraceIndent, Options), 291 write(Out, '}') 292 ). 293:- endif. 294pp(Term, Ctx, Options) :- % handle operators 295 compound(Term), 296 compound_name_arity(Term, Name, Arity), 297 current_op(Prec, Type, Name), 298 match_op(Type, Arity, Kind, Prec, Left, Right), 299 option(operators(true), Options), 300 !, 301 quoted_op(Name, QName), 302 option(output(Out), Options), 303 context(Ctx, indent, Indent), 304 context(Ctx, depth, Depth), 305 context(Ctx, priority, CPrec), 306 NDepth is Depth + 1, 307 modify_context(Ctx, [depth=NDepth], Ctx1), 308 dec_depth(Ctx1, Ctx2), 309 LeftOptions = Ctx2.put(priority, Left), 310 FuncOptions = Ctx2.put(embrace, never), 311 RightOptions = Ctx2.put(priority, Right), 312 ( Kind == prefix 313 -> arg(1, Term, Arg), 314 ( ( space_op(Name) 315 ; need_space(Name, Arg, FuncOptions, RightOptions) 316 ) 317 -> Space = ' ' 318 ; Space = '' 319 ), 320 ( CPrec >= Prec 321 -> format(atom(Buf), '~w~w', [QName, Space]), 322 atom_length(Buf, AL), 323 NIndent is Indent + AL, 324 write(Out, Buf), 325 modify_context(Ctx2, [indent=NIndent, priority=Right], Ctx3), 326 pp(Arg, Ctx3, Options) 327 ; format(atom(Buf), '(~w', [QName,Space]), 328 atom_length(Buf, AL), 329 NIndent is Indent + AL, 330 write(Out, Buf), 331 modify_context(Ctx2, [indent=NIndent, priority=Right], Ctx3), 332 pp(Arg, Ctx3, Options), 333 format(Out, ')', []) 334 ) 335 ; Kind == postfix 336 -> arg(1, Term, Arg), 337 ( ( space_op(Name) 338 ; need_space(Name, Arg, FuncOptions, LeftOptions) 339 ) 340 -> Space = ' ' 341 ; Space = '' 342 ), 343 ( CPrec >= Prec 344 -> modify_context(Ctx2, [priority=Left], Ctx3), 345 pp(Arg, Ctx3, Options), 346 format(Out, '~w~w', [Space,QName]) 347 ; format(Out, '(', []), 348 NIndent is Indent + 1, 349 modify_context(Ctx2, [indent=NIndent, priority=Left], Ctx3), 350 pp(Arg, Ctx3, Options), 351 format(Out, '~w~w)', [Space,QName]) 352 ) 353 ; arg(1, Term, Arg1), 354 arg(2, Term, Arg2), 355 ( ( space_op(Name) 356 ; need_space(Arg1, Name, LeftOptions, FuncOptions) 357 ; need_space(Name, Arg2, FuncOptions, RightOptions) 358 ) 359 -> Space = ' ' 360 ; Space = '' 361 ), 362 ( CPrec >= Prec 363 -> modify_context(Ctx2, [priority=Left], Ctx3), 364 pp(Arg1, Ctx3, Options), 365 format(Out, '~w~w~w', [Space,QName,Space]), 366 modify_context(Ctx2, [priority=Right], Ctx4), 367 pp(Arg2, Ctx4, Options) 368 ; format(Out, '(', []), 369 NIndent is Indent + 1, 370 modify_context(Ctx2, [indent=NIndent, priority=Left], Ctx3), 371 pp(Arg1, Ctx3, Options), 372 format(Out, '~w~w~w', [Space,QName,Space]), 373 modify_context(Ctx2, [priority=Right], Ctx4), 374 pp(Arg2, Ctx4, Options), 375 format(Out, ')', []) 376 ) 377 ). 378pp(Term, Ctx, Options) :- % compound 379 option(output(Out), Options), 380 option(indent_arguments(IndentStyle), Options), 381 context(Ctx, indent, Indent), 382 ( IndentStyle == false 383 -> pprint(Term, Ctx, Options) 384 ; IndentStyle == auto, 385 print_width(Term, Width, Options), 386 option(right_margin(RM), Options), 387 Indent + Width < RM % fits on a line, simply write 388 -> pprint(Term, Ctx, Options) 389 ; Term =.. [Name|Args], 390 format(atom(Buf2), '~q(', [Name]), 391 write(Out, Buf2), 392 atom_length(Buf2, FunctorIndent), 393 ( integer(IndentStyle) 394 -> Nindent is Indent + IndentStyle, 395 ( FunctorIndent > IndentStyle 396 -> indent(Out, Nindent, Options) 397 ; true 398 ) 399 ; Nindent is Indent + FunctorIndent 400 ), 401 context(Ctx, depth, Depth), 402 NDepth is Depth + 1, 403 modify_context(Ctx, [indent=Nindent, depth=NDepth], NCtx0), 404 dec_depth(NCtx0, NCtx), 405 pp_compound_args(Args, NCtx, Options), 406 write(Out, ')') 407 ). 408 409 410quoted_op(Op, Atom) :- 411 is_solo(Op), 412 !, 413 Atom = Op. 414quoted_op(Op, Q) :- 415 format(atom(Q), '~q', [Op]). 416 417pp_list_elements(_, Ctx, Options) :- 418 context(Ctx, max_depth, 0), 419 !, 420 option(output(Out), Options), 421 write(Out, '...'). 422pp_list_elements([H|T], Ctx0, Options) :- 423 dec_depth(Ctx0, Ctx), 424 pp(H, Ctx, Options), 425 ( T == [] 426 -> true 427 ; nonvar(T), 428 T = [_|_] 429 -> option(output(Out), Options), 430 write(Out, ','), 431 context(Ctx, indent, Indent), 432 indent(Out, Indent, Options), 433 pp_list_elements(T, Ctx, Options) 434 ; option(output(Out), Options), 435 context(Ctx, indent, Indent), 436 indent(Out, Indent-2, Options), 437 write(Out, '| '), 438 pp(T, Ctx, Options) 439 ). 440 441 442pp_compound_args([H|T], Ctx, Options) :- 443 pp(H, Ctx, Options), 444 ( T == [] 445 -> true 446 ; T = [_|_] 447 -> option(output(Out), Options), 448 write(Out, ','), 449 context(Ctx, indent, Indent), 450 indent(Out, Indent, Options), 451 pp_compound_args(T, Ctx, Options) 452 ; option(output(Out), Options), 453 context(Ctx, indent, Indent), 454 indent(Out, Indent-2, Options), 455 write(Out, '| '), 456 pp(T, Ctx, Options) 457 ). 458 459 460:- if(current_predicate(is_dict/1)). 461pp_dict_args([Name-Value|T], Ctx, Options) :- 462 option(output(Out), Options), 463 line_position(Out, Pos0), 464 pp(Name, Ctx, Options), 465 write(Out, ':'), 466 line_position(Out, Pos1), 467 context(Ctx, indent, Indent), 468 Indent2 is Indent + Pos1-Pos0, 469 modify_context(Ctx, [indent=Indent2], Ctx2), 470 pp(Value, Ctx2, Options), 471 ( T == [] 472 -> true 473 ; option(output(Out), Options), 474 write(Out, ','), 475 indent(Out, Indent, Options), 476 pp_dict_args(T, Ctx, Options) 477 ). 478:- endif. 479 480% match_op(+Type, +Arity, +Precedence, -LeftPrec, -RightPrec 481 482match_op(fx, 1, prefix, P, _, R) :- R is P - 1. 483match_op(fy, 1, prefix, P, _, P). 484match_op(xf, 1, postfix, P, _, L) :- L is P - 1. 485match_op(yf, 1, postfix, P, P, _). 486match_op(xfx, 2, infix, P, A, A) :- A is P - 1. 487match_op(xfy, 2, infix, P, L, P) :- L is P - 1. 488match_op(yfx, 2, infix, P, P, R) :- R is P - 1. 489 490 491%! indent(+Out, +Indent, +Options) 492% 493% Newline and indent to the indicated column. Respects the option 494% =tab_width=. Default is 8. If the tab-width equals zero, 495% indentation is emitted using spaces. 496 497indent(Out, Indent, Options) :- 498 option(tab_width(TW), Options, 8), 499 nl(Out), 500 ( TW =:= 0 501 -> tab(Out, Indent) 502 ; Tabs is Indent // TW, 503 Spaces is Indent mod TW, 504 forall(between(1, Tabs, _), put(Out, 9)), 505 tab(Out, Spaces) 506 ). 507 508%! print_width(+Term, -W, +Options) is det. 509% 510% Width required when printing `normally' left-to-right. 511 512print_width(Term, W, Options) :- 513 option(right_margin(RM), Options), 514 ( write_length(Term, W, [max_length(RM)|Options]) 515 -> true 516 ; W = RM 517 ). 518 519%! pprint(+Term, +Context, +Options) 520% 521% The bottom-line print-routine. 522 523pprint(Term, Ctx, Options) :- 524 option(output(Out), Options), 525 pprint(Out, Term, Ctx, Options). 526 527pprint(Out, Term, Ctx, Options) :- 528 option(write_options(WriteOptions), Options), 529 context(Ctx, max_depth, MaxDepth), 530 ( MaxDepth == infinite 531 -> write_term(Out, Term, WriteOptions) 532 ; MaxDepth =< 0 533 -> format(Out, '...', []) 534 ; write_term(Out, Term, [max_depth(MaxDepth)|WriteOptions]) 535 ). 536 537 538 /******************************* 539 * SHARED WITH term_html.pl * 540 *******************************/ 541 542 543%! is_op1(+Name, -Type, -Priority, -ArgPriority, +Options) is semidet. 544% 545% True if Name is an operator taking one argument of Type. 546 547is_op1(Name, Type, Pri, ArgPri, Options) :- 548 operator_module(Module, Options), 549 current_op(Pri, OpType, Module:Name), 550 argpri(OpType, Type, Pri, ArgPri), 551 !. 552 553argpri(fx, prefix, Pri0, Pri) :- Pri is Pri0 - 1. 554argpri(fy, prefix, Pri, Pri). 555argpri(xf, postfix, Pri0, Pri) :- Pri is Pri0 - 1. 556argpri(yf, postfix, Pri, Pri). 557 558%! is_op2(+Name, -LeftPri, -Pri, -RightPri, +Options) is semidet. 559% 560% True if Name is an operator taking two arguments of Type. 561 562is_op2(Name, LeftPri, Pri, RightPri, Options) :- 563 operator_module(Module, Options), 564 current_op(Pri, Type, Module:Name), 565 infix_argpri(Type, LeftPri, Pri, RightPri), 566 !. 567 568infix_argpri(xfx, ArgPri, Pri, ArgPri) :- ArgPri is Pri - 1. 569infix_argpri(yfx, Pri, Pri, ArgPri) :- ArgPri is Pri - 1. 570infix_argpri(xfy, ArgPri, Pri, Pri) :- ArgPri is Pri - 1. 571 572 573%! need_space(@Term1, @Term2, +LeftOptions, +RightOptions) 574% 575% True if a space is needed between Term1 and Term2 if they are 576% printed using the given option lists. 577 578need_space(T1, T2, _, _) :- 579 ( is_solo(T1) 580 ; is_solo(T2) 581 ), 582 !, 583 fail. 584need_space(T1, T2, LeftOptions, RightOptions) :- 585 end_code_type(T1, TypeR, LeftOptions.put(side, right)), 586 end_code_type(T2, TypeL, RightOptions.put(side, left)), 587 \+ no_space(TypeR, TypeL). 588 589no_space(punct, _). 590no_space(_, punct). 591no_space(quote(R), quote(L)) :- 592 !, 593 R \== L. 594no_space(alnum, symbol). 595no_space(symbol, alnum). 596 597%! end_code_type(+Term, -Code, Options) 598% 599% True when code is the first/last character code that is emitted 600% by printing Term using Options. 601 602end_code_type(_, Type, Options) :- 603 MaxDepth = Options.max_depth, 604 integer(MaxDepth), 605 Options.depth >= MaxDepth, 606 !, 607 Type = symbol. 608end_code_type(Term, Type, Options) :- 609 primitive(Term, _), 610 !, 611 quote_atomic(Term, S, Options), 612 end_type(S, Type, Options). 613end_code_type(Dict, Type, Options) :- 614 is_dict(Dict, Tag), 615 !, 616 ( Options.side == left 617 -> end_code_type(Tag, Type, Options) 618 ; Type = punct 619 ). 620end_code_type('$VAR'(Var), Type, Options) :- 621 Options.get(numbervars) == true, 622 !, 623 format(string(S), '~W', ['$VAR'(Var), [numbervars(true)]]), 624 end_type(S, Type, Options). 625end_code_type(List, Type, _) :- 626 ( List == [] 627 ; List = [_|_] 628 ), 629 !, 630 Type = punct. 631end_code_type(OpTerm, Type, Options) :- 632 compound_name_arity(OpTerm, Name, 1), 633 is_op1(Name, Type, Pri, ArgPri, Options), 634 \+ Options.get(ignore_ops) == true, 635 !, 636 ( Pri > Options.priority 637 -> Type = punct 638 ; ( Type == prefix 639 -> end_code_type(Name, Type, Options) 640 ; arg(1, OpTerm, Arg), 641 arg_options(Options, ArgOptions), 642 end_code_type(Arg, Type, ArgOptions.put(priority, ArgPri)) 643 ) 644 ). 645end_code_type(OpTerm, Type, Options) :- 646 compound_name_arity(OpTerm, Name, 2), 647 is_op2(Name, LeftPri, Pri, _RightPri, Options), 648 \+ Options.get(ignore_ops) == true, 649 !, 650 ( Pri > Options.priority 651 -> Type = punct 652 ; arg(1, OpTerm, Arg), 653 arg_options(Options, ArgOptions), 654 end_code_type(Arg, Type, ArgOptions.put(priority, LeftPri)) 655 ). 656end_code_type(Compound, Type, Options) :- 657 compound_name_arity(Compound, Name, _), 658 end_code_type(Name, Type, Options). 659 660end_type(S, Type, Options) :- 661 number(S), 662 !, 663 ( (S < 0 ; S == -0.0), 664 Options.side == left 665 -> Type = symbol 666 ; Type = alnum 667 ). 668end_type(S, Type, Options) :- 669 Options.side == left, 670 !, 671 sub_string(S, 0, 1, _, Start), 672 syntax_type(Start, Type). 673end_type(S, Type, _) :- 674 sub_string(S, _, 1, 0, End), 675 syntax_type(End, Type). 676 677syntax_type("\"", quote(double)) :- !. 678syntax_type("\'", quote(single)) :- !. 679syntax_type("\`", quote(back)) :- !. 680syntax_type(S, Type) :- 681 string_code(1, S, C), 682 ( code_type(C, prolog_identifier_continue) 683 -> Type = alnum 684 ; code_type(C, prolog_symbol) 685 -> Type = symbol 686 ; code_type(C, space) 687 -> Type = layout 688 ; Type = punct 689 ). 690 691is_solo(Var) :- 692 var(Var), !, fail. 693is_solo(','). 694is_solo(';'). 695is_solo('!'). 696 697%! primitive(+Term, -Class) is semidet. 698% 699% True if Term is a primitive term, rendered using the CSS 700% class Class. 701 702primitive(Term, Type) :- var(Term), !, Type = 'pl-avar'. 703primitive(Term, Type) :- atom(Term), !, Type = 'pl-atom'. 704primitive(Term, Type) :- string(Term), !, Type = 'pl-string'. 705primitive(Term, Type) :- integer(Term), !, Type = 'pl-int'. 706primitive(Term, Type) :- rational(Term), !, Type = 'pl-rational'. 707primitive(Term, Type) :- float(Term), !, Type = 'pl-float'. 708 709%! operator_module(-Module, +Options) is det. 710% 711% Find the module for evaluating operators. 712 713operator_module(Module, Options) :- 714 Module = Options.get(module), 715 !. 716operator_module(TypeIn, _) :- 717 '$module'(TypeIn, TypeIn). 718 719%! arg_options(+Options, -OptionsOut) is det. 720% 721% Increment depth in Options. 722 723arg_options(Options, Options.put(depth, NewDepth)) :- 724 NewDepth is Options.depth+1. 725 726quote_atomic(Float, String, Options) :- 727 float(Float), 728 Format = Options.get(float_format), 729 !, 730 format(string(String), Format, [Float]). 731quote_atomic(Plain, Plain, _) :- 732 number(Plain), 733 !. 734quote_atomic(Plain, String, Options) :- 735 Options.get(quoted) == true, 736 !, 737 ( Options.get(embrace) == never 738 -> format(string(String), '~q', [Plain]) 739 ; format(string(String), '~W', [Plain, Options]) 740 ). 741quote_atomic(Var, String, Options) :- 742 var(Var), 743 !, 744 format(string(String), '~W', [Var, Options]). 745quote_atomic(Plain, Plain, _). 746 747space_op(:-). 748