1:- module(quantity, [ quantity/0, quantity/3 ]).
    2:- use_module(library(dcg/basics)).
    3
    4:- set_prolog_flag(float_overflow, infinity).
    5:- set_prolog_flag(float_undefined, nan).
    6:- set_prolog_flag(float_zero_div, infinity).
    7
    8% Parse quantity
    9quantity(Number, Options, String) :-
   10    string(String),
   11    string_codes(String, Codes),
   12    quantity(Number, Options, Codes).
   13
   14quantity(Number, Options, Atom) :-
   15    atom(Atom),
   16    atom_codes(Atom, Codes),
   17    quantity(Number, Options, Codes).
   18
   19quantity(Number, Options, [H | Codes]) :-
   20    quantity(Number, Options, [H | Codes], []).
   21
   22quantity(Number, Options, [H | Codes]) :-
   23    interval(Number, Options, [H | Codes], []).
   24
   25% Examples
   26quantity :-
   27    quantity("2.5").
   28
   29quantity :-
   30    quantity("+2.5").
   31
   32quantity :-
   33    quantity("-2.5").
   34
   35quantity :-
   36    quantity(".5").
   37
   38quantity :-
   39    quantity("-.5").
   40
   41quantity :-
   42    quantity("-5").
   43
   44quantity :-
   45    quantity("5").
   46
   47quantity :-
   48    quantity("-3.3 to -3.2").
   49
   50quantity(String) :-
   51    quantity(N, Options, String),
   52    writeln(string(String)-number(N)-options(Options)).
   53
   54% Main types
   55:- discontiguous quantity//2.
   56
   57quantity(Q, [type(natural)])
   58--> nat(Q).
   59
   60quantity(Q, [type(integer) | Options])
   61--> int(Q, Options).
   62
   63quantity(Q, [type(real) | Options])
   64--> real(Q, Options).
   65
   66interval(ci(Lo, Hi), Options)
   67--> quantity(Lo, LoOpt),
   68    to(ToOpt),
   69    quantity(Hi, HiOpt),
   70    { append([LoOpt, ToOpt, HiOpt], Options) }.
   71
   72% Components
   73sign(+1, [sign(none)])
   74--> "".
   75
   76sign(+1, [sign(plus)])
   77--> "+".
   78
   79sign(-1, [sign(hyphen)])
   80--> "-".
   81
   82sign(-1, [sign(dash)])
   83--> [226, 136, 146].
   84
   85sign(-1, [sign(minus)])
   86--> [8722].
   87
   88nat(N)
   89--> digits([H | Codes]),
   90    { number_codes(N, [H | Codes]) }.
   91
   92int(I, Options)
   93--> sign(S, Options),
   94    nat(N),
   95    { I is S * N }.
   96
   97sep(dot)
   98--> ".".
   99
  100sep(comma)
  101--> ",".
  102
  103frac(F, [frac(given), sep(S), digits(D)])
  104--> sep(S),
  105    digits([H | Codes]),
  106    { number_codes(N, [H | Codes]),
  107      length(Codes, L),
  108      D is L + 1,
  109      F is N / 10^D
  110    }.
  111
  112real(R, [int(given) | Options])
  113--> sign(S, Options),
  114    "∞",
  115    { R is S * 1.0Inf }.
  116
  117real(R, [int(given) | Options])
  118--> sign(S, Options),
  119    "oo",
  120    { R is S * 1.0Inf }.
  121
  122real(R, [int(given) | Options])
  123--> sign(S, Options),
  124    "OO",
  125    { R is S * 1.0Inf }.
  126
  127real(R, [int(given) | Options])
  128--> sign(S, Options),
  129    "oO",
  130    { R is S * 1.0Inf }.
  131
  132real(R, [int(given) | Options])
  133--> sign(S, Options),
  134    "inf",
  135    { R is S * 1.0Inf }.
  136
  137real(R, [int(given) | Options])
  138--> sign(S, Options),
  139    "Inf",
  140    { R is S * 1.0Inf }.
  141
  142real(R, [int(given) | Options])
  143--> sign(S, Options),
  144    "infty",
  145    { R is S * 1.0Inf }.
  146
  147real(R, [int(given) | Options])
  148--> sign(S, Options),
  149    "Infty",
  150    { R is S * 1.0Inf }.
  151
  152real(R, [int(given) | Options])
  153--> sign(S, Opt1),
  154    nat(1),
  155    frac(0, Opt2),
  156    "Inf",
  157    { R is S * 1.0Inf,
  158      append([Opt1, Opt2], Options)
  159    }.
  160
  161% 1.23
  162real(R, [int(given) | Options])
  163--> sign(S, Opt1),
  164    nat(N),
  165    frac(F, Opt2),
  166    { R is S * (N + F),
  167      append([Opt1, Opt2], Options)
  168    }.
  169
  170% .77
  171real(R, [int(none) | Options])
  172--> sign(S, Opt1),
  173    frac(F, Opt2),
  174    { R is S * F,
  175      append([Opt1, Opt2], Options)
  176    }.
  177
  178% 12
  179real(R, [int(given), frac(none) | Options])
  180--> sign(S, Options),
  181    nat(N),
  182    { R is S * N }.
  183
  184% Intervals (e.g., confidence intervals)
  185to([to(to)])
  186--> blank, blanks, "to", blank, blanks.
  187
  188to([to(dotdotdot)])
  189--> blank, blanks, "...", blank, blanks.
  190
  191to([to(dash)])
  192--> blank, blanks, [226, 136, 146], blank, blanks.
  193
  194to([to(hyphen)])
  195--> blank, blanks, "-", blank, blanks