? users online
  • Logout
    • Open hangout
    • Open chat for current file
<div class="notebook">

<div class="nb-cell markdown" name="md1">
# Parsing quantities

My students keep forgetting the units, so I want to give feedback to their numerical inputs.
</div>

<div class="nb-cell query" name="q1">
quantity("1.5 kg", N, Options)
</div>

<div class="nb-cell program" data-background="true" name="p1">
:- use_module(library(dcg/basics)).

%
% General numeric input like "1.5 cm"
%
quantity(String, Number, Options) :-
    ground(String),
    string_codes(String, Codes), 
    quantity(Number, Options, Codes, []).

match_quantities(Solution, Response, []) :-
    quantity(Solution, Number_sol, Options_sol),
    option(dec(Dec), Options_sol),
    quantity(Response, Number_res, Options_res),
    option(dec(Dec), Options_res),
    Number_sol = Number_res.
    
match_quantities(Solution, Response, [buggy(dec(Response) \= Sol)]) :-
    quantity(Solution, Number_sol, Options_sol),
    option(dec(Sol), Options_sol),
    quantity(Response, Number_res, Options_res),
    option(dec(Res), Options_res),
    Sol \= Res,
    N is round(Number_sol * 10^min(Sol, Res)) / 10^min(Sol, Res),
    N is round(Number_res * 10^min(Sol, Res)) / 10^min(Sol, Res).
    
%
% 1.5E10
%
quantity(Number, Options) --&gt; 
    number(Number, Options).

%
% 10 kg
%
quantity(Number, Options) --&gt; 
    number(Num, NumOpt),
    blanks,
    unit(_Unit, UnitOpt),
    { option(fac(Kilo), UnitOpt, 1),
      Number is Num * Kilo, 
      option(dec(D), NumOpt, 0),
      Dec is D - log10(Kilo),
      merge_options([dec(Dec) | UnitOpt], NumOpt, Options)
    }.

% Real number
number(Number, Options) --&gt;
    intdotfrac(Int, IntOpt),
    power(Pow),
    { Number is Int * 10^Pow, 
      option(dec(D), IntOpt, 0),
      Dec is D - Pow,
      merge_options([dec(Dec)], IntOpt, Options) 
    }.

% sign(?Number, ?Options, ?Codes)//
sign(+1, [sign(+)]) --&gt; 
    "+".

sign(-1, [sign(-)]) --&gt; 
    "-".

sign(-1, [sign(-)]) --&gt; 
    [226, 136, 146].

sign(1, [sign(none)]) --&gt; 
    [].

% Natural number
nat(Number) --&gt;
    digits([H | T]),
    { number_codes(Number, [H | T]) }.

dot --&gt; ".".
dot --&gt; ",".

% Fractional part
frac(Number, [dec(Dec)]) --&gt; 
    digits([H | T]),
    { number_codes(Num, [H | T]), 
      length([H | T], Dec), 
      Number is Num / 10.0^Dec
    }.

% 15
intdotfrac(Number, [dec(0) | Options]) --&gt;
    sign(Sign, Options),
    nat(Int),
    { Number is Sign * Int }.
    
% 15.5
intdotfrac(Number, Options) --&gt;
    sign(Sign, SgnOpt),
    nat(Int),
    dot,
    frac(Frac, FracOpt),
    { Number is Sign * (Int + Frac),
      merge_options(SgnOpt, FracOpt, Options)
    }.

% .70
intdotfrac(Number, Options) --&gt;
    sign(Sign, SgnOpt),
    dot,
    frac(Frac, FracOpt),
    { Number is Sign * Frac,
      merge_options(SgnOpt, [int(empty) | FracOpt], Options)
    }.
    
% times 10^E
power(Pow) --&gt; 
    exp_e, 
    sign(Sign, _),
    nat(P),
    { Pow is Sign * P }.

power(0) --&gt; [].

exp_e --&gt; "E".
exp_e --&gt; "e".
exp_e --&gt; blanks, "*", blanks, "10", blanks, "^", blanks.
exp_e --&gt; blanks, "*", blanks, "10", blanks, "**", blanks.
exp_e --&gt; blanks, [195, 151], blanks, "10", blanks, "^", blanks.
exp_e --&gt; blanks, [195, 151], blanks, "10", blanks, "**", blanks.

unit('%', [fac(0.01), si('%')]) --&gt; "%".

unit(U, [si(SI) | Options]) --&gt; 
    modifier(M, Options), 
    si(SI),
    { string_concat(M, SI, U) }.

% Modifier
modifier(k, [fac(1000)]) --&gt; "k".
modifier(c, [fac(0.01)]) --&gt; "c".
modifier(m, [fac(0.001)]) --&gt; "m".
modifier('', [fac(1)]) --&gt; "".

% Unit
si(g) --&gt; "g".
si(m) --&gt; "m".

ex_quant :-
    S = ".5 kg", 
    quantity(S, N, Options),
    writeln(S), writeln(N), writeln(Options).

ex_quant :-
    S = "1.5 kg",
    quantity(S, N, Options),
    writeln(S), writeln(N), writeln(Options).

ex_quant :-
    S = "15 kg",
    quantity(S, N, Options),
    writeln(S), writeln(N), writeln(Options).

ex_quant :-
    S = "15 g",
    quantity(S, N, Options),
    writeln(S), writeln(N), writeln(Options).

ex_quant :-
    S = "-15 g",
    quantity(S, N, Options),
    writeln(S), writeln(N), writeln(Options).

ex_quant :-
    S = "-15E10 g",
    quantity(S, N, Options),
    writeln(S), writeln(N), writeln(Options).

ex_quant :-
    S = "-1.5E-10 g",
    quantity(S, N, Options),
    writeln(S), writeln(N), writeln(Options).

ex_quant :-
    S = "0.09",
    quantity(S, N, Options),
    writeln(S), writeln(N), writeln(Options).

ex_quant :-
    S = "9%",
    quantity(S, N, Options),
    writeln(S), writeln(N), writeln(Options).

ex_quant :-
    S = "9.1 %",
    quantity(S, N, Options),
    writeln(S), writeln(N), writeln(Options).

ex_quant :-
    Sol = "3.14", Res = "3.141",
    match_quantities(Sol, Res, Comment),
    writeln(Sol), writeln(Res), writeln(Comment).

ex_quant :-
    Sol = "3.141", Res = "3.14",
    match_quantities(Sol, Res, Comment),
    writeln(Sol), writeln(Res), writeln(Comment).

ex_quant :-
    Sol = "3.14", Res = "3.14",
    match_quantities(Sol, Res, Comment),
    writeln(Sol), writeln(Res), writeln(Comment).
</div>

</div>