Prolog: simple lexer/2 - prolog

I need a small lexer/2 in prolog, currently I have
tokens(Z) --> "while", tokens(Y), {Z = [ttwhile | Y]}.
tokens(Z) --> "do", tokens(Y), {Z = [ttdo | Y]}.
tokens(Z) --> "endwhile", tokens(Y), {Z = [ttendwhile | Y]}.
tokens(Z) --> "repeat", tokens(Y), {Z = [ttrepeat | Y]}.
tokens(Z) --> "until", tokens(Y), {Z = [ttuntil | Y]}.
tokens(Z) --> "endrepeat", tokens(Y), {Z = [ttendrepeat | Y]}.
tokens(Z) --> "if", tokens(Y), {Z = [ttif | Y]}.
tokens(Z) --> "then", tokens(Y), {Z = [ttthen | Y]}.
tokens(Z) --> "else", tokens(Y), {Z = [ttelse | Y]}.
tokens(Z) --> "endif", tokens(Y), {Z = [ttendif | Y]}.
tokens(Z) --> "exit", tokens(Y), {Z = [ttexit | Y]}.
tokens(Z) --> "other", tokens(Y), {Z = [ttother | Y]}.
% Comparison operators.
tokens(Z) --> "==", tokens(Y), {Z = [equal | Y]}.
tokens(Z) --> "<>", tokens(Y), {Z = [notequal | Y]}.
% Assignment operator.
tokens(Z) --> ":=", tokens(Y), {Z = [:= | Y]}.
% Boolean constants and operators.
tokens(Z) --> "true", tokens(Y), {Z = [true | Y]}.
tokens(Z) --> "false", tokens(Y), {Z = [false | Y]}.
tokens(Z) --> "and", tokens(Y), {Z = [and | Y]}.
tokens(Z) --> "or", tokens(Y), {Z = [or | Y]}.
tokens(Z) --> " ", tokens(Y), {Z = Y}.
tokens(Z) --> " ", tokens(Y), {Z = Y}.
tokens(Z) --> [C], tokens(Y), {name(X, [C]), Z = [X | Y]}.
tokens(Z) --> [], {Z = []}.
Can anyone help me with the next step for lexer/2 so that when I call
lexer([while,a,==,b,do,abc,endwhile], R), I could get R = [ttwhile, a, equal, b, ttdo, abc, ttendwhile]?
Thank you very much.

well, this 'glue' - more or less - solves your request:
lexer(L, Tokens) :-
atomic_list_concat(L, ' ', A),
atom_codes(A, Cs),
phrase(tokens(Tokens), Cs).
?- lexer([while,a,==,b,do,abc,endwhile], R).
R = [ttwhile, a, equal, b, ttdo, a, b, c, ttendwhile] ;
R = [ttwhile, a, equal, b, ttdo, a, b, c, e|...] ;
but you should really rewrite in declarative style:
token(ttwhile) --> "while".
token(ttendwhile) --> "endwhile".
token(ttdo) --> "do".
%...
token(equal) --> "==".
token(notequal) --> "<>".
token(assign) --> ":=".
% this is wrong: symbols overlap with alphabetic tokens
token(N) --> [C], {atom_codes(N,[C])}.
tokens([]) --> [].
tokens(Ts) --> " ", tokens(Ts).
tokens([T|Ts]) --> token(T), tokens(Ts).
lexer(Cs, Tokens) :-
phrase(tokens(Tokens), Cs).
and call passing a codes list, a double quoted (or backquoted, if you're using SWI) string
?- lexer(`while abc endwhile`, R).
R = [ttwhile, a, b, c, ttendwhile] ;
R = [ttwhile, a, b, c, e, n, d, ttwhile] ;
...
edit
to tokenize names (well, only lowercase, for simplicity), replace the above token(N) --> [C], {atom_codes(N,[C])}. with
token(N) --> lower_case_chars(Cs), {Cs \= [], atom_codes(N,Cs)}.
lower_case_chars([C|Cs]) --> lower_case_char(C), lower_case_chars(Cs).
lower_case_chars([]) --> [].
lower_case_char(C) --> [C], {C>=0'a, C=<0'z}.
but it becomes a little verbose, when you add also upper_case_chars, digits, etc... it's worth to generalize, passing the characters range boundary, or use code_type/2:
token(N) --> csymf(C), csyms(Cs), {atom_codes(N,[C|Cs])}.
csymf(C) --> [C], {code_type(C,csymf)}.
csyms([C|Cs]) --> [C], {code_type(C,csym)}, csyms(Cs).
csyms([]) --> [].

What about the following solution?
lexer(I, O) :-
tokens(O, I, []).
But calling lexer() in this way
lexer("while a == b do abc endwhile", R)
I add a suggestion: rewrite tokens() in this way
tokens([ttwhile | Z]) --> "while", tokens(Z).
tokens([ttdo | Z]) --> "do", tokens(Z).
tokens([endwhile | Z]) --> "endwhile", tokens(Z).
tokens([ttrepeat | Z]) --> "repeat", tokens(Z).
tokens([ttuntil | Z]) --> "until", tokens(Z).
tokens([ttendrepeat | Z]) --> "endrepeat", tokens(Z).
tokens([if | Z]) --> "if", tokens(Z).
tokens([then |Z]) --> "then", tokens(Z).
tokens([ttelse | Z]) --> "else", tokens(Z).
tokens([ttendif | Z]) --> "endif", tokens(Z).
tokens([ttexit | Z]) --> "exit", tokens(Z).
tokens([ttother | Z]) --> "other", tokens(Z).
% Comparison operators.
tokens([equal | Z]) --> "==", tokens(Z).
tokens([notequal | Z]) --> "<>", tokens(Z).
% Assignment operator.
tokens([:= | Z]) --> ":=", tokens(Z).
% Boolean constants and operators.
tokens([true | Z]) --> "true", tokens(Z).
tokens([false | Z]) --> "false", tokens(Z).
tokens([and | Z]) --> "and", tokens(Z).
tokens([or | Z]) --> "or", tokens(Z).
tokens(Z) --> " ", tokens(Z).
tokens([X | Z]) --> [C], tokens(Z), {name(X, [C])}.
tokens([]) --> [].
P.s.: sorry for my bad English.

Related

run length encoding using DCGs

problem from: https://web.archive.org/web/20200718175929/http://www.pathwayslms.com/swipltuts/dcg/
Use a dcg to convert a sparse sequence like
[0,0,0,0,0,0,7,4,3,0,0,0,0,0,0,0,8,9,14,0,0,0,0....]
to
[zero(6), 7,4,3, zero(7), 8,9,14, ...]
i feel like i understand the material on this page up to here but don't really know how to start this one. any pointers would be appreciated
Try doing something like this:
code([X|Xs]) --> item(X), code(Xs).
code([]) --> [].
item(X) --> [0], zeros(1, X).
item(X) --> [X], { X \= 0 }.
zeros(M, N) --> [0], { M1 is M + 1 }, zeros(M1, N).
zeros(N, zero(N)) --> \+ [0].
Example:
?- phrase(code(C), [0,0,0,0,0,0,7,4,3,0,0,0,0,0,0,0,8,9,14,0,0,0,0]).
C = [zero(6), 7, 4, 3, zero(7), 8, 9, 14, zero(4)] ;
false.
Alternate style:
% For eos
:- use_module(library(dcg/basics)).
list_rle(Lst, LstRle) :-
must_be(list, Lst),
phrase(rle, Lst, LstRle),
% The first is correct
!.
% Check for list of zeros
rle, [zero(N)] --> [0], zeros(1, N), rle.
% Accept anything otherwise
rle, [C] --> [C], rle.
% Being greedy - check for end last
% eos means no more input - better than [] which can leave a remainder
rle --> eos.
zeros(N, Tot) --> [0], { N1 is N + 1 }, zeros(N1, Tot).
% Being greedy - check for end last
zeros(N, N) --> [].
Result in swi-prolog:
?- time(list_rle([0,0,0,0,0,0,7,4,3,0,0,0,0,0,0,0,8,9,14,0,0,0,0], L)).
% 39 inferences, 0.000 CPU in 0.000 seconds (92% CPU, 545073 Lips)
L = [zero(6),7,4,3,zero(7),8,9,14,zero(4)].
Another minor variant which can also be used as a generator:
rle --> eos.
rle, [zero(N)] --> [0], zeros(1, N), rle.
rle, [C] --> [C], { dif(C, 0) }, rle.
zeros(N, Tot) --> [0], !, { N1 is N + 1 }, zeros(N1, Tot).
zeros(N, N) --> [].
Result:
?- length(L, _), list_rle(L, LR).
L = LR, LR = [] ;
L = [0],
LR = [zero(1)] ;
L = LR, LR = [_A],
dif(_A,0) ;
L = [0,0],
LR = [zero(2)] ;
L = [_A,0],
LR = [_A,zero(1)],
dif(_A,0) ;
L = LR, LR = [_A,_B],
dif(_A,0),
dif(_B,0) ;
L = [0,0,0],
LR = [zero(3)] ;
L = [_A,0,0],
LR = [_A,zero(2)],
dif(_A,0) ;

Clausify Prolog Error

I have a project in PROLOG to convert an FBF function to a CNF function, where fbf2cnf is the predicate to change the function.
When I run this instruction:
fbf2cnf(every(X, exist(Y, foo(Y, X))), F).
... it throws this error:
ERROR: =../2: Type error: `atomic' expected, found `foo(skf1(_G1758),_G1758)' (a compound)
Exception: (7) fbf2cnf(every(_G1558, exist(_G1557, foo(_G1557, _G1558))), _G1567) ?
How can I avoid throwing this exception?
The code is here:
exist(V, FBF) :-
compound(FBF),
skolem_function([], SK),
exist_private(V, SK, [FBF], [], _),
V = SK.
exist_private(V, SK, [H | []], NL, Final) :-
variabile(H),
H == V,
append([SK], NL, NL2),
Final = NL2, !.
exist_private(V, SK, [H | T], NL, Final) :-
variabile(H),
H == V,
exist_private(V, SK, T, NL, F2),
append([SK], F2, NL2),
Final = NL2, !.
exist_private(_, _, [H | []], NL, Final) :-
termine(H),
append([H], NL, NL2),
Final = NL2, !.
exist_private(_, _, [H | T], NL, Final) :-
termine(H),
exist_private(_, _, T, NL, F2),
append([H], F2, NL2),
Final = NL2, !.
exist_private(V, SK, [H | []], _, Final) :-
compound(H),
H =.. [H1 | L],
exist_private(V, SK, L, [], F2),
append([H1], F2, NH),
FH =.. NH,
append([FH], [], NL2),
Final = NL2, !.
exist_private(V, SK, [H | T], NL, Final) :-
compound(H),
H =.. [H1 | L],
exist_private(V, SK, L, [], F2),
append([H1], F2, NH),
FH =.. NH,
exist_private(V, SK, T, NL, F3),
append([FH], F3, NL2),
Final = NL2, !.
fbf2cnf(FBF, CNFFBF) :-
termine(FBF),
CNFFBF = FBF, !.
fbf2cnf(FBF, CNFFBF) :-
predicato(FBF),
FBF =.. [H | T],
H \= not,
H \= and,
H \= or,
H \= implies,
H \= exist,
H \= every,
T \=[],
valid_fbf(FBF),
CNFFBF = FBF ,!.
fbf2cnf(FBF, CNFFBF) :-
compound(FBF),
FBF =.. [H | _],
H \= exist,
valid_fbf(FBF),
fbf2cnf_private([FBF], N),
CNFFBF = N, !.
fbf2cnf(FBF, CNFFBF) :-
compound(FBF),
FBF =.. [H | T],
H = exist,
T = [V | T2],
NFBF =.. T2,
fbf2cnf(NFBF, FBF2),
FBF2 =.. L,
skolem_function([], SK),
exist_private(V, SK, L, [], F),
V = SK,
Final =.. F,
fbf2cnf(Final, CNF),
CNFFBF = CNF, !.
fbf2cnf(FBF, CNFFBF) :-
compound(FBF),
FBF =.. [H | T],
H = every,
T = [V | T2],
T2 = [H2 | _ ],
H2 =.. [Op | T3],
T3 = [H3 | T4],
Op = exist,
skolem_function([V], SF),
exist_private(H3, SF, T4, [], NL),
H3 = SF,
NFBF =.. NL,
fbf2cnf(NFBF, NL2),
CNFFBF = NL2, !.
fbf2cnf(FBF, CNFFBF) :-
compound(FBF),
FBF =.. [H | T],
H = every,
T = [_ | T2],
T2 = [H2 | _ ],
H2 =.. [Op | _],
Op \= exist,
fbf2cnf(H2, CNF),
CNFFBF = CNF, !.
/*----------------------------------------- */
fbf2cnf_private([H | _], CNFFBF) :-
termine(H),
CNFFBF = H, !.
fbf2cnf_private([H | _], CNF) :-
H =.. [H1 | T],
H1 = exist,
T \= [],
T = [_ | T2],
FBF =.. T2,
exist(V, FBF),
CNF = V.
fbf2cnf_private(L, CNF) :-
L = [H | _],
H =.. [H1 | Ls],
H1 = and,
valid_fbf(H),
and(H1, Ls, CNF1),
CNF2 =.. CNF1,
flatten_and(CNF2, NL),
append([and], NL, Temp),
Final =.. Temp,
CNF = Final, !.
fbf2cnf_private(L, CNF) :-
L = [H | _],
H =.. [H1 | Ls],
H1 = or,
valid_fbf(H),
or(H1, Ls, CNF1),
CNF2 =.. CNF1,
flatten_or(CNF2, NL),
append([or], NL, Temp),
Final =.. Temp,
CNF = Final, !.
fbf2cnf_private([H | _], CNF) :-
H =.. [H1 | _],
H1 = not,
not_counter(H, M),
CNF = M.
fbf2cnf_private([H | _], CNF) :-
H =.. [H1 | T],
H1 = implies,
implies(T, M),
fbf2cnf(M, M1),
CNF = M1, !.

Erlang "case" clause for values in a range

Learning Erlang, I'm solving a simple problem and I came up with this solution:
%%%------------------------------------------------------------------
%%% #doc https://leetcode.com/problems/add-two-numbers/
%%%
%%% #end
%%%------------------------------------------------------------------
-module(add_two_numbers).
-define(BASE10, 10).
-export([main/2]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
-spec main(L1 :: nonempty_list(non_neg_integer()), L2 :: nonempty_list(non_neg_integer())) -> list().
%%%==================================================================
%%% Export
%%%==================================================================
main(L1, L2) ->
loop(L1, L2, 0, []).
-ifdef(TEST).
main_test() ->
?assertEqual([0, 2, 1, 2, 1, 1], add_two_numbers:main([9, 6, 9, 5, 9], [1, 5, 1, 6, 1])),
?assertEqual([3, 6, 9, 7, 5], add_two_numbers:main([4, 1, 8, 7, 2], [9, 4, 1, 0, 3])),
?assertEqual([6, 7, 9, 0, 1], add_two_numbers:main([2, 2, 3, 3], [4, 5, 6, 7])),
?assertEqual([6, 3, 7, 4, 1], add_two_numbers:main([4, 1, 0, 8], [2, 2, 7, 6])),
?assertEqual([2, 7, 9, 1], add_two_numbers:main([2, 7, 1, 0], [0, 0, 8, 1])),
?assertEqual([8, 9, 9, 1], add_two_numbers:main([9, 9, 9], [9, 9, 9])),
?assertEqual([7, 1, 6, 1], add_two_numbers:main([9, 3, 7], [8, 7, 8])),
?assertEqual([3, 5, 6, 1], add_two_numbers:main([6, 6, 6], [7, 8, 9])),
?assertEqual([0, 0, 0], add_two_numbers:main([0, 0, 0], [0, 0, 0])),
?assertEqual([7, 0, 8], add_two_numbers:main([2, 4, 3], [5, 6, 4])),
?assertEqual([0, 2, 2], add_two_numbers:main([0, 1], [0, 1, 2])),
?assertEqual([0, 1, 1], add_two_numbers:main([4, 6], [6, 4])),
?assertEqual([0, 0, 1], add_two_numbers:main([1], [9, 9])),
?assertEqual([0, 1], add_two_numbers:main([], [0, 1])),
?assertEqual([], add_two_numbers:main([], [])),
?assertError(badarith, add_two_numbers:main([0, 1, 2], ["", 5, 6])).
-endif.
%%%==================================================================
%%% Internal
%%%==================================================================
loop([H1 | T1], [H2 | T2], C, R) when H1 + H2 + C >= ?BASE10 ->
loop(T1, T2, 1, R ++ [H1 + H2 + C - ?BASE10]);
loop([], [H | T], C, R) when H + C >= ?BASE10 ->
loop([], T, 1, R ++ [H + C - ?BASE10]);
loop([H | T], [], C, R) when H + C >= ?BASE10 ->
loop([], T, 1, R ++ [H + C - ?BASE10]);
loop([H1 | T1], [H2 | T2], C, R) ->
loop(T1, T2, 0, R ++ [H1 + H2 + C]);
loop([], [H | T], C, R) ->
loop([], T, 0, R ++ [H + C]);
loop([H | T], [], C, R) ->
loop([], T, 1, R ++ [H + C]);
loop([], [], C, R) when C > 0 -> R ++ [C];
loop([], [], _, R) -> R.
What bothers me is how many loop calls I have to define. Eventually there might be better solutions for this.
My question: is there any way I can tell in a case-of if some condition is within a range? Something like this, for instance:
X = H1 + H2 + C,
case X of
X >= 10 -> blah;
_ -> ummm
end.
UPDATE
This is what I was trying to achieve:
loop([H1 | T1], [H2 | T2], C, R) ->
case H1 + H2 + C >= ?BASE10 of
true -> loop(T1, T2, 1, R ++ [H1 + H2 + C - ?BASE10]);
false -> loop(T1, T2, 0, R ++ [H1 + H2 + C])
end;
loop([], [H | T], C, R) ->
case H + C >= ?BASE10 of
true -> loop([], T, 1, R ++ [H + C - ?BASE10]);
false -> loop([], T, 0, R ++ [H + C])
end;
loop([H | T], [], C, R) ->
case H + C >= ?BASE10 of
true -> loop(T, [], 1, R ++ [H + C - ?BASE10]);
false -> loop(T, [], 0, R ++ [H + C])
end;
loop([], [], C, R) ->
case C > 0 of
true -> R ++ [C];
false -> R
end.
...not sure if it's better though.
You could use this little trick
loop([], [], 0, R) -> lists:reverse(R);
loop([], [], 1, R) -> lists:reverse(R, [1]);
loop([], L2, C, R) ->
loop([0], L2, C, R);
loop(L1, [], C, R) ->
loop(L1, [0], C, R);
loop([H1 | T1], [H2 | T2], C, R) ->
case H1 + H2 + C of
S when S >= ?BASE10 ->
loop(T1, T2, 1, [S - ?BASE10 | R]);
S ->
loop(T1, T2, 0, [S | R])
end.
Note I don't use Acc++[X] pattern because it is bad habit make it O(N^2)(See[1]). From performance point of view tail call optimization is not necessary if you have to use lists:reverse/1,2 so this non-tail call version should be as fast as tail call optimised or on some platforms even faster:
main(L1, L2) ->
loop(L1, L2, 0).
loop([], [], 0) -> [];
loop([], [], 1) -> [1];
loop([], L2, C) ->
loop([0], L2, C);
loop(L1, [], C) ->
loop(L1, [0], C);
loop([H1 | T1], [H2 | T2], C) ->
case H1 + H2 + C of
X when X >= ?BASE10 ->
[ X - ?BASE10 | loop(T1, T2, 1) ];
X ->
[ X | loop(T1, T2, 0) ]
end.
Or you can make one step further and remove case and also get + 0 out
loop([], [], 0) -> [];
loop([], [], 1) -> [1];
loop([], [H | T], C) ->
loop_([], T, H + C);
loop([H | T], [], C) ->
loop_([], T, H + C);
loop([H1 | T1], [H2 | T2], C) ->
loop_(T1, T2, H1 + H2 + C).
loop_(L1, L2, S) when S >= ?BASE10 ->
[ S - ?BASE10 | loop(L1, L2, 1) ];
loop_(L1, L2, S) ->
[ S | loop(L1, L2, 0) ].
[1]: Try [L1, L2] = [ [rand:uniform(10)-1 || _ <- lists:seq(1, 100000)] || _ <- [1,2] ], timer:tc(fun() -> add_two_numbers:main(L1, L2) end). with your code. In my code it takes 3.5ms and yours 33s.
You could use 2 helper functions (Not sure it is more efficient, maybe easier to read):
loop([H1 | T1], [H2 | T2], C, R) ->
N = H1 + H2 + C,
loop(T1, T2, carry(N), R ++ sum(N));
loop([], [H | T], C, R) ->
N = H + C,
loop([], T, carry(N), R ++ sum(N));
loop([H | T], [], C, R) ->
N = H + C,
loop(T, [], carry(N), R ++ sum(N));
loop([], [], 0, R) ->
R;
loop([], [], C, R) ->
R ++ [C].
carry(N) when N >= ?BASE10 -> 1;
carry(_) -> 0.
sum(N) when N >= ?BASE10 -> [N - ?BASE10];
sum(N) -> [N].

Prolog calculator simply returns true

I'm writing a calculator in Prolog that reads natural language questions and returns a number answer for a class assignment, and I'm nearly complete. However, when I input a sentence the program simply returns 'Yes' and then quits. As far as I can tell it doesn't even read in the sentence. This is my first time writing in Prolog, so I have no clue what is wrong. Any help would be greatly appreciated.
My code:
:- consult('aux.p').
accumulator(0).
start :-
write('Cranky Calculator'), nl,
write('-----------------'), nl,
cvt.
cvt :-
write('What do ya want?'), nl,
read_sentence(Question),
butlast(Question, Questio),
Questio \== [quit], !,
(
phrase(sentence(Value), Questio, []),
write(Value);
write_string('Stop it with your gibberish!')
), nl,
cvt.
cvt.
reset(V) :-
retract(accumulator(_)),
assert(accumulator(V)).
accumulate('plus', N, Value) :-
{Temp is accumulator(_)},
{Value is Temp + N},
reset(Value).
accumulate('minus', N, Value) :-
{Temp is accumulator(_)},
{Value is Temp - N},
reset(Value).
accumulate('divided', N, Value) :-
{Temp is accumulator(_)},
{Value is Temp / N},
reset(Value).
accumulate('times', N, Value) :-
{Temp is accumulator(_)},
{Value is Temp * N},
reset(Value).
accumulate(N1, 'plus', N2, Value) :-
{Value is N1 + N2},
reset(Value).
accumulate(N1, 'minus', N2, Value) :-
{Value is N1 - N2},
reset(Value).
accumulate(N1, 'divided', N2, Value) :-
{Value is N1 / N2},
reset(Value).
accumulate(N1, 'times', N2, Value) :-
{Value is N1 * N2},
reset(Value).
%------------------base productions---------------------
% sentence can be to an entirely new question or simply be an addition
% to the previous one
sentence(V) --> base(V1), {V is V1}.
sentence(V) --> additional(V1), {V is V1}.
sentence --> [].
base(Value) -->
pro, be, number(N1), oper(OP), number(N2), qmark,
{
accumulate(N1, OP, N2, V), {Value is V}
}.
additional(Value) -->
oper(OP), number(N), qmark,
{
accumulate(OP, N, V), {Value is V}
}.
pro --> [what].
pro --> [how], [much].
be --> [is].
number(N) --> five_digit(N1), {N is N1}.
five_digit(N) --> ten_thousands(V1), four_digit(V2), {N is 1000 * V1 + V2}.
four_digit(N) --> thousands(V1), three_digit(V2), {N is 1000 * V1 + V2}.
three_digit(N) --> hundreds(V1), two_digit(V2), {N is 100 * V1 + V2}.
two_digit(N) --> tens(V1), one_digit(V2), {N is V1 + V2}.
two_digit(N) --> teens(V), {N is V}.
one_digit(N) --> digit(V), {N is V}.
one_digit(0) --> [].
ten_thousands(T) --> tens(V), thousand, {T is V}.
ten_thousands(T) --> tens(V), {T is V}.
ten_thousands(T) --> teens(V), thousand, {T is V}.
ten_thousands(0) --> [].
thousands(T) --> digit(V), thousand, {T is V}.
thousands(0) --> [].
hundreds(T) --> digit(V), hundred, {T is V}.
hundreds(0) --> [].
thousand --> [thousand].
hundred --> [hundred].
digit(1) --> [one].
digit(2) --> [two].
digit(3) --> [three].
digit(4) --> [four].
digit(5) --> [five].
digit(6) --> [six].
digit(7) --> [seven].
digit(8) --> [eight].
digit(9) --> [nine].
tens(20) --> [twenty].
tens(30) --> [thirty].
tens(40) --> [fourty].
tens(50) --> [fifty].
tens(60) --> [sixty].
tens(70) --> [seventy].
tens(80) --> [eighty].
tens(90) --> [ninety].
teens(10) --> [ten].
teens(11) --> [eleven].
teens(12) --> [twelve].
teens(13) --> [thirteen].
teens(14) --> [fourteen].
teens(15) --> [fifteen].
teens(16) --> [sixteen].
teens(17) --> [seventeen].
teens(18) --> [eighteen].
teens(19) --> [nineteen].
oper(plus) --> [plus].
oper(plus) --> [and].
oper(minus) --> [minus].
oper(divided) --> ['divided by'].
oper(times) --> [times].
qmark --> ['?'].
The output I get looks like:
|: what is twelve plus two?
Yes
I took your code as a spec for a calculator that also gives the
result as text. The idea here is to combine DCG and CLP(FD).
CLP(FD) is constraint solving for finite domains. Finite domains
should be enough for your calculator. To enable CLP(FD) you have
first to load the appropriate library. In Jekejeke Minlog this
is done as follows:
:- ensure_loaded(library('clpfd.px')).
The code has first a section that can not only recognize numbers
but also generate text for numbers. This is mainly the part where
DCGs are combined with CLP(FD):
number(N) --> {N #= 1000 * V1 + 100 * V2 + V3}, thousands(V1),
hundreds(V2), two_digit_opt(V3).
thousands(N) --> two_digit(N), thousand.
thousands(0) --> [].
thousand --> [thousand].
hundreds(N) --> digit(N), hundred.
hundreds(0) --> [].
hundred --> [hundred].
two_digit_opt(N) --> two_digit(N).
two_digit_opt(0) --> [].
two_digit(N) --> {N #= V1*10 + V2}, tens(V1), digit_opt(V2).
two_digit(N) --> {N #= V+10}, teens(V).
two_digit(N) --> digit(N).
digit_opt(N) --> digit(N).
digit_opt(0) --> [].
digit(1) --> [one].
digit(2) --> [two].
digit(3) --> [three].
digit(4) --> [four].
digit(5) --> [five].
digit(6) --> [six].
digit(7) --> [seven].
digit(8) --> [eight].
digit(9) --> [nine].
tens(2) --> [twenty].
tens(3) --> [thirty].
tens(4) --> [fourty].
tens(5) --> [fifty].
tens(6) --> [sixty].
tens(7) --> [seventy].
tens(8) --> [eighty].
tens(9) --> [ninety].
teens(0) --> [ten].
teens(1) --> [eleven].
teens(2) --> [twelve].
teens(3) --> [thirteen].
teens(4) --> [fourteen].
teens(5) --> [fifteen].
teens(6) --> [sixteen].
teens(7) --> [seventeen].
teens(8) --> [eighteen].
teens(9) --> [nineteen].
Here is a prove that the bidirectionality works:
?- phrase(number(X),[fifty,five]).
X = 55 ;
No
?- phrase(number(55),X).
X = [fifty,five] ;
No
Adding the calculator was straight forward. I didn't use assert/retract,
I simply using an argument in an infinite loop. I don't know how healthy
this is for your Prolog system, especially since we now inbetween touch
the constraint store. At least in Jekejeke Minlog as of version 0.7.2
the constraint store will not yet be completely recycled, so that one
cannot run the loop indefinitely.
But to show how all the pieces can be put together, the loop solution
is fine. The code reads as follows:
loop(S) :-
write('> '),
flush_output,
read(L),
phrase(cmd(C),L),
do(C,S,T),
phrase(number(T),M),
write(M), nl,
!, loop(T).
loop(S) :-
write('?'), nl,
loop(S).
do(set(N),_,N).
do(add(N),S,T) :- T is S+N.
do(sub(N),S,T) :- T is S-N.
cmd(set(N)) --> factor(N).
cmd(add(N)) --> [plus], factor(N).
cmd(sub(N)) --> [minus], factor(N).
factor(M) --> number(N), more(N, M).
more(N, M) --> [times], number(H), {J is N*H}, more(J,M).
more(N, M) --> [divided, by], number(H), {J is N//H}, more(J,M).
more(N, N) --> [].
And here is an example execution:
?- loop(0).
> [eleven,times,eleven].
[one,hundred,twenty,one]
> [minus,sixty,six].
[fifty,five]
Here is a little how to for the Jekejeke CLP(FD)
Jekejeke Minlog Desktop Installation
https://www.youtube.com/watch?v=6ZipaIrxSFQ
Jekejeke Minlog Android Installation
https://www.youtube.com/watch?v=Y2P7cEuOIws

SWI-Prolog tokenize_atom/2 replacement?

What I need to do is to break atom to tokens. E. g.:
tokenize_string('Hello, World!', L).
would unify L=['Hello',',','World','!']. Exactly as tokenize_atom/2 do. But when I try to use tokenize_atom/2 with non-latin letters it fails. Is there any universal replacement or how I can write one? Thanks in advance.
Well, you could write your own lexer. For example I can show you a lexer from my arithmetic expressions parser.
:- use_module(library(http/dcg_basics)).
%
% lexer
%
lex([H | T]) -->
lexem_t(H), !,
lex(T).
lex([]) -->
[].
lexem_t(L) --> trashes, lexem(L), trashes.
trashes --> trash, !, trashes.
trashes --> [].
trash --> comment_marker(End), !, string(_), End.
trash --> white.
comment_marker("*)") --> "(*".
comment_marker("*/") --> "/*".
hex_start --> "0X".
hex_start --> "0x".
lexem(open) --> "(".
lexem(close) --> ")".
lexem(+) --> "+".
lexem(-) --> "-".
lexem(*) --> "*".
lexem(/) --> "/".
lexem(^) --> "^".
lexem(,) --> ",".
lexem(!) --> "!".
lexem(N) --> hex_start, !, xinteger(N). % this handles hex numbers
lexem(N) --> number(N). % this handles integers/floats
lexem(var(A)) --> identifier_c(L), {string_to_atom(L, A)}.
identifier_c([H | T]) --> alpha(H), !, many_alnum(T).
alpha(H) --> [H], {code_type(H, alpha)}.
alnum(H) --> [H], {code_type(H, alnum)}.
many_alnum([H | T]) --> alnum(H), !, many_alnum(T).
many_alnum([]) --> [].
How it works:
?- phrase(lex(L), "abc 123 привет 123.4e5 !+- 0xabc,,,"), write(L).
[var(abc), 123, var(привет), 1.234e+007, !, +, -, 2748, (,), (,), (,)]

Resources