The Great Documentation Efort, a small refactor and small bug fixes
This commit is contained in:
parent
b4a94461a7
commit
dfdd65abdf
395
polymani.pl
395
polymani.pl
|
@ -46,52 +46,66 @@ polyplay :-
|
||||||
prompt(OldPrompt, '> '),
|
prompt(OldPrompt, '> '),
|
||||||
%% Read a line as codes, from the 'user_input' stream
|
%% Read a line as codes, from the 'user_input' stream
|
||||||
read_line_to_codes(user_input, InCodes),
|
read_line_to_codes(user_input, InCodes),
|
||||||
|
%% Restore old prompt
|
||||||
prompt(_, OldPrompt),
|
prompt(_, OldPrompt),
|
||||||
|
%% Split the input at spaces and ignore \r and \t
|
||||||
split_string(InCodes, " ", "\r\t", LS),
|
split_string(InCodes, " ", "\r\t", LS),
|
||||||
|
%% Convert each set of codes into a term or atom, as appropriate
|
||||||
maplist(name, LA, LS),
|
maplist(name, LA, LS),
|
||||||
(
|
(
|
||||||
|
%% If we read a 'bye', terminate
|
||||||
LA == [bye],
|
LA == [bye],
|
||||||
write("See ya"),
|
write("See ya"),
|
||||||
nl,
|
nl,
|
||||||
!
|
!
|
||||||
;
|
;
|
||||||
|
(
|
||||||
|
%% Parse the input into a tree
|
||||||
|
parse_input(TIn, LA, NC),
|
||||||
(
|
(
|
||||||
parse_input(TIn, LA, NC),
|
%% If the tree is empty, it means nothing was understood
|
||||||
(
|
TIn == void,
|
||||||
TIn == void,
|
writeln("I didn't understand what you want."),
|
||||||
writeln("I didn't understand what you want."),
|
writeln(NC)
|
||||||
writeln(NC)
|
|
||||||
;
|
|
||||||
(
|
|
||||||
NC \== [],
|
|
||||||
write("Syntax error in token: "),
|
|
||||||
cons(H, _, NC),
|
|
||||||
write(H),
|
|
||||||
nl
|
|
||||||
;
|
|
||||||
(
|
|
||||||
debug_print(true),
|
|
||||||
write(LA),
|
|
||||||
nl,
|
|
||||||
write(TIn),
|
|
||||||
nl
|
|
||||||
;
|
|
||||||
process_input(TIn)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
;
|
;
|
||||||
writeln("I didn't understand what you want.")
|
(
|
||||||
),
|
%% If there is unconsumed input, it means there was a syntatic error
|
||||||
polyplay
|
NC \== [],
|
||||||
|
write("Syntax error in token: "),
|
||||||
|
%% Take the head of the list
|
||||||
|
cons(H, _, NC),
|
||||||
|
write(H),
|
||||||
|
nl
|
||||||
|
;
|
||||||
|
(
|
||||||
|
%% If the debug_print flag is true, print some extract debug info
|
||||||
|
debug_print(true),
|
||||||
|
write(LA),
|
||||||
|
nl,
|
||||||
|
write(TIn),
|
||||||
|
nl
|
||||||
|
;
|
||||||
|
%% Otherwise, process the parse tree and execute the commands within
|
||||||
|
process_input(TIn)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
%% Parsing failed
|
||||||
|
writeln("I didn't understand what you want.")
|
||||||
),
|
),
|
||||||
!.
|
%% Go back to the beginning
|
||||||
|
polyplay
|
||||||
|
),
|
||||||
|
!.
|
||||||
|
|
||||||
%% Flag which determines whether to print the parsed input or to process it
|
%% Flag which determines whether to print the parsed input or to process it
|
||||||
debug_print(false).
|
debug_print(false).
|
||||||
|
|
||||||
process_input(command(CL, void)) :-
|
%% process_input(+tree) is determines
|
||||||
do_process_input(CL).
|
%
|
||||||
|
% Execute the commands from the parse tree
|
||||||
|
%
|
||||||
process_input(command(CL, TCR)) :-
|
process_input(command(CL, TCR)) :-
|
||||||
%% Process first command
|
%% Process first command
|
||||||
do_process_input(CL),
|
do_process_input(CL),
|
||||||
|
@ -99,98 +113,147 @@ process_input(command(CL, TCR)) :-
|
||||||
TCR \== void,
|
TCR \== void,
|
||||||
%% recurse
|
%% recurse
|
||||||
process_input(TCR).
|
process_input(TCR).
|
||||||
|
process_input(command(CL, void)) :-
|
||||||
|
%% Process only command left
|
||||||
|
do_process_input(CL).
|
||||||
|
|
||||||
|
%% do_process_input(+tree) is det
|
||||||
|
%
|
||||||
|
% Process a single command from the input
|
||||||
|
% Takes, as the only argument, the tree representing
|
||||||
|
% the work to be done
|
||||||
|
%
|
||||||
do_process_input(show_stored_polynomials) :-
|
do_process_input(show_stored_polynomials) :-
|
||||||
|
%% If the command is 'show_stored_polynomials'
|
||||||
|
%% Store in D the list of all results of
|
||||||
|
%% polynomial_store, which is a dynamic predicate
|
||||||
findall(nm(X,Y), polynomial_store(X,Y), D),
|
findall(nm(X,Y), polynomial_store(X,Y), D),
|
||||||
|
%% Print the list of stored variables
|
||||||
print_all_stored_variables(D).
|
print_all_stored_variables(D).
|
||||||
do_process_input(show(store(P), T)) :-
|
do_process_input(show(store(P), T)) :-
|
||||||
( polynomial_store(P, _),
|
%% If the command was to store and show
|
||||||
retractall(polynomial_store(P, _)); true ),
|
%% Delegate the work of storing
|
||||||
!,
|
do_process_input(store(P, T)),
|
||||||
assertz(polynomial_store(P, T)),
|
%% Show the variable and the polynomial. Delegate
|
||||||
write(P),
|
do_process_input(show(P, T)).
|
||||||
write(" = "),
|
|
||||||
polynomial_tree_to_polynomial(T, Pl),
|
|
||||||
write(Pl),
|
|
||||||
nl.
|
|
||||||
do_process_input(show(load(P), void)) :-
|
do_process_input(show(load(P), void)) :-
|
||||||
|
%% Make sure there's a variable
|
||||||
P \== void,
|
P \== void,
|
||||||
(
|
(
|
||||||
|
%% Check if the variable is stored
|
||||||
polynomial_store(P, T),
|
polynomial_store(P, T),
|
||||||
write(P),
|
%% Show the variable and the polynomial. Delegate
|
||||||
write(" = "),
|
do_process_input(show(P, T))
|
||||||
polynomial_tree_to_polynomial(T, Pl),
|
|
||||||
write(Pl),
|
|
||||||
nl
|
|
||||||
;
|
;
|
||||||
write("Variable not stored"),
|
writeln("Variable not stored")
|
||||||
nl
|
|
||||||
).
|
).
|
||||||
|
do_process_input(show(void, T)) :-
|
||||||
|
%% Make sure there's a tree
|
||||||
|
T \== void,
|
||||||
|
%% Convert it to a flat polynomial
|
||||||
|
polynomial_tree_to_polynomial(T, Pl),
|
||||||
|
writeln(Pl).
|
||||||
do_process_input(show(P, T)) :-
|
do_process_input(show(P, T)) :-
|
||||||
|
%% Make sure the arguments are in the right format and not one
|
||||||
|
%% that should be handled by other clauses
|
||||||
P \== void,
|
P \== void,
|
||||||
P \== store(_),
|
P \== store(_),
|
||||||
|
P \== load(_),
|
||||||
T \== void,
|
T \== void,
|
||||||
|
%% Write out the variable and the normal
|
||||||
|
%% representation of the polynomial
|
||||||
write(P),
|
write(P),
|
||||||
write(" = "),
|
write(" = "),
|
||||||
|
%% Convert the input tree to a flat polynomial, to print
|
||||||
polynomial_tree_to_polynomial(T, Pl),
|
polynomial_tree_to_polynomial(T, Pl),
|
||||||
write(Pl),
|
writeln(Pl).
|
||||||
nl.
|
|
||||||
do_process_input(show(void, T)) :-
|
|
||||||
T \== void,
|
|
||||||
polynomial_tree_to_polynomial(T, Pl),
|
|
||||||
write(Pl),
|
|
||||||
nl.
|
|
||||||
do_process_input(store(P, T)) :-
|
do_process_input(store(P, T)) :-
|
||||||
|
(
|
||||||
|
%% If there is a polynomial stored with the same name
|
||||||
|
polynomial_store(P, _),
|
||||||
|
%% Delete all occurences of it
|
||||||
|
retractall(polynomial_store(P, _))
|
||||||
|
;
|
||||||
|
%% Otherwise, move forward and don't do anything special
|
||||||
|
true
|
||||||
|
),
|
||||||
|
!,
|
||||||
|
%% Dynamically add to the end of the predicate, effectively storing it
|
||||||
assertz(polynomial_store(P, T)).
|
assertz(polynomial_store(P, T)).
|
||||||
do_process_input(forget(P)) :-
|
do_process_input(forget(P)) :-
|
||||||
retract(polynomial_store(P, _)).
|
%% Remove all stored predicates with this variable
|
||||||
|
retractall(polynomial_store(P, _)).
|
||||||
do_process_input(simplify(PT)) :-
|
do_process_input(simplify(PT)) :-
|
||||||
|
%% Convert the polynomial to it's flat representation
|
||||||
polynomial_tree_to_polynomial(PT, P),
|
polynomial_tree_to_polynomial(PT, P),
|
||||||
|
%% Use simpoly from the UI layer to simplify, so it
|
||||||
|
%% gives nice error messages
|
||||||
simpoly(P, SP),
|
simpoly(P, SP),
|
||||||
write(SP),
|
writeln(SP).
|
||||||
nl.
|
|
||||||
do_process_input(multiply(TN, PT)) :-
|
do_process_input(multiply(TN, PT)) :-
|
||||||
|
%% To multiply, assume the left is a number
|
||||||
|
%% Flatten both
|
||||||
polynomial_tree_to_polynomial(TN, N),
|
polynomial_tree_to_polynomial(TN, N),
|
||||||
polynomial_tree_to_polynomial(PT, P),
|
polynomial_tree_to_polynomial(PT, P),
|
||||||
|
%% Use function from UI layer
|
||||||
scalepoly(N, P, P2),
|
scalepoly(N, P, P2),
|
||||||
|
%% Simplify
|
||||||
simpoly(P2, SP),
|
simpoly(P2, SP),
|
||||||
write(SP),
|
writeln(SP).
|
||||||
nl.
|
|
||||||
|
|
||||||
|
|
||||||
%% print_all_stored_variables
|
%% print_all_stored_variables
|
||||||
%
|
%
|
||||||
% Prints the stored variables
|
% Prints the stored variables, given the list
|
||||||
%
|
%
|
||||||
print_all_stored_variables([nm(X,T) | TS]) :-
|
print_all_stored_variables([nm(X,T) | TS]) :-
|
||||||
|
%% Print
|
||||||
write(X),
|
write(X),
|
||||||
write(" = "),
|
write(" = "),
|
||||||
polynomial_tree_to_polynomial(T, P),
|
polynomial_tree_to_polynomial(T, P),
|
||||||
write(P),
|
writeln(P),
|
||||||
nl,
|
%% Recurse on the right
|
||||||
print_all_stored_variables(TS).
|
print_all_stored_variables(TS).
|
||||||
print_all_stored_variables([]).
|
print_all_stored_variables([]).
|
||||||
|
|
||||||
|
%% polynomial_tree_to_polynomial(+tree, -polynomial) is det
|
||||||
|
%
|
||||||
|
% Flatten a polynomail tree into a simple polynomial
|
||||||
|
%
|
||||||
polynomial_tree_to_polynomial(op(Op, TL, TR), P) :-
|
polynomial_tree_to_polynomial(op(Op, TL, TR), P) :-
|
||||||
|
%% If it matched on the version with a node, don't go back
|
||||||
!,
|
!,
|
||||||
|
%% Recurse on the left and right
|
||||||
polynomial_tree_to_polynomial(TL, PL),
|
polynomial_tree_to_polynomial(TL, PL),
|
||||||
polynomial_tree_to_polynomial(TR, PR),
|
polynomial_tree_to_polynomial(TR, PR),
|
||||||
|
%% Convert the results of the recursion to atoms
|
||||||
term_to_atom(PL, TermL),
|
term_to_atom(PL, TermL),
|
||||||
term_to_atom(PR, TermR),
|
term_to_atom(PR, TermR),
|
||||||
|
%% Concat the parts
|
||||||
atom_concat(TermL, Op, Temp),
|
atom_concat(TermL, Op, Temp),
|
||||||
atom_concat(Temp, TermR, PA),
|
atom_concat(Temp, TermR, PA),
|
||||||
|
%% Convert back to terms
|
||||||
term_to_atom(P, PA).
|
term_to_atom(P, PA).
|
||||||
polynomial_tree_to_polynomial(load(P), Pl) :-
|
polynomial_tree_to_polynomial(load(P), Pl) :-
|
||||||
|
%% If there's a load tag, don't go back
|
||||||
|
!,
|
||||||
|
%% Retrieve the polynomial from the store
|
||||||
polynomial_store(P, TP),
|
polynomial_store(P, TP),
|
||||||
|
%% Recurse as if there was a normal tree here
|
||||||
polynomial_tree_to_polynomial(TP, Pl).
|
polynomial_tree_to_polynomial(TP, Pl).
|
||||||
|
%% If we get here, it means we didn't match any of the previous
|
||||||
|
%% clauses, so the tree is already flat
|
||||||
polynomial_tree_to_polynomial(T, T).
|
polynomial_tree_to_polynomial(T, T).
|
||||||
%% Tests:
|
|
||||||
%% ?- polynomial_tree_to_polynomial(op(+, 2, op(+, 2, op(*, 1, y))), S).
|
|
||||||
%@ S = 2+2+1*y.
|
|
||||||
|
|
||||||
%% nlp_number(?W:Atom, ?D:Int) is det
|
%% special_word_number(?W:Atom, ?D:Int) is det
|
||||||
%
|
%
|
||||||
% Definition of a Alphabetical and Numerical relation
|
% Definition of a Alphabetical and Numerical relation
|
||||||
|
% The third argument refers to different types of precedence:
|
||||||
|
% - f means a single unit digit, which doesn't
|
||||||
|
% combine on the right, but can on the left
|
||||||
|
% - g means a number which doesn't combine left or right
|
||||||
|
% - fy means a number which can optionally combine on the right
|
||||||
|
% - xfy means a number which requires a number on the left and
|
||||||
|
% an optional one on the right
|
||||||
%
|
%
|
||||||
special_word_number(zero, 0, f).
|
special_word_number(zero, 0, f).
|
||||||
special_word_number(a, 1, f).
|
special_word_number(a, 1, f).
|
||||||
|
@ -228,58 +291,106 @@ special_word_number(million, 1000000, xfy).
|
||||||
%% special_word_number(third, 0.33333, xf).
|
%% special_word_number(third, 0.33333, xf).
|
||||||
%% special_word_number(quarter, 0.25, xf).
|
%% special_word_number(quarter, 0.25, xf).
|
||||||
|
|
||||||
|
%% parse_number_explicit(+precedence, +tree_left, -tree,
|
||||||
%% nlp_number(?W:Atom, ?D:Int) is det
|
%% +stream, -not_consumed) is det
|
||||||
%
|
%
|
||||||
% Definition of a Alphabetical and Numerical relation
|
% Use the precedence of the last number and the tree
|
||||||
|
% on the left to create the tree on the right, parsing
|
||||||
|
% the input stream.
|
||||||
|
%
|
||||||
|
% You probably want to call this with void in the first
|
||||||
|
% two arguments. They are for internal use
|
||||||
%
|
%
|
||||||
|
|
||||||
%% Entry point
|
|
||||||
parse_number_explicit(void, void, T, [WN | In], NC) :-
|
parse_number_explicit(void, void, T, [WN | In], NC) :-
|
||||||
|
%% Entry point
|
||||||
|
%% Convert a word to a number with precedence f, g, or fy
|
||||||
special_word_number(WN, N, P),
|
special_word_number(WN, N, P),
|
||||||
member(P, [f, g, fy]),
|
member(P, [f, g, fy]),
|
||||||
!,
|
!,
|
||||||
|
%% Recurse with the previous precedence
|
||||||
|
%% and tree as arguments. The same in the other clauses
|
||||||
parse_number_explicit(P, N, T, In, NC).
|
parse_number_explicit(P, N, T, In, NC).
|
||||||
parse_number_explicit(fy, NL, T, [WN | In], NC) :-
|
parse_number_explicit(fy, NL, T, [WN | In], NC) :-
|
||||||
|
%% If we read an fy before and now an f
|
||||||
special_word_number(WN, N, f),
|
special_word_number(WN, N, f),
|
||||||
!,
|
!,
|
||||||
|
%% Add them on the left tree and recurse
|
||||||
parse_number_explicit(f, op(+, NL, N), T, In, NC).
|
parse_number_explicit(f, op(+, NL, N), T, In, NC).
|
||||||
parse_number_explicit(xfy, TL, T, [WN | In], NC) :-
|
parse_number_explicit(xfy, TL, T, [WN | In], NC) :-
|
||||||
|
%% Parsed an xfy before, now parse any number on the right
|
||||||
TL \= void,
|
TL \= void,
|
||||||
special_word_number(WN, N, P),
|
special_word_number(WN, N, P),
|
||||||
member(P, [f, g, fy]),
|
member(P, [f, g, fy]),
|
||||||
!,
|
!,
|
||||||
|
%% Add the parts
|
||||||
parse_number_explicit(P, op(+, TL, N), T, In, NC).
|
parse_number_explicit(P, op(+, TL, N), T, In, NC).
|
||||||
parse_number_explicit(_, TL, T, [WN | In], NC) :-
|
parse_number_explicit(_, TL, T, [WN | In], NC) :-
|
||||||
|
%% Whatever the precedence on the left
|
||||||
special_word_number(WN, N, xfy),
|
special_word_number(WN, N, xfy),
|
||||||
|
%% Enforce there was a left argument
|
||||||
TL \= void,
|
TL \= void,
|
||||||
!,
|
!,
|
||||||
|
%% Multiply this and the left
|
||||||
parse_number_explicit(xfy, op(*, TL, N), T, In, NC).
|
parse_number_explicit(xfy, op(*, TL, N), T, In, NC).
|
||||||
parse_number_explicit(P, TL, T, [and, WN | In], NC) :-
|
parse_number_explicit(P, TL, T, [and, WN | In], NC) :-
|
||||||
|
%% And is part of a valid number if there's a valid
|
||||||
|
%% number on the left and right
|
||||||
special_word_number(WN, _, _),
|
special_word_number(WN, _, _),
|
||||||
|
%% Passthrough
|
||||||
parse_number_explicit(P, TL, T, [WN | In], NC),
|
parse_number_explicit(P, TL, T, [WN | In], NC),
|
||||||
!.
|
!.
|
||||||
parse_number_explicit(_, T, T, [WN | In], [WN | In]) :-
|
parse_number_explicit(_, T, T, [WN | In], [WN | In]) :-
|
||||||
|
%% If there's a tree
|
||||||
T \= void,
|
T \= void,
|
||||||
|
%% And can't consume more words as
|
||||||
|
%% numbers (fail on first), then we're done
|
||||||
not(special_word_number(WN, _, _)),
|
not(special_word_number(WN, _, _)),
|
||||||
!.
|
!.
|
||||||
parse_number_explicit(_, T, T, [], []) :-
|
parse_number_explicit(_, T, T, [], []) :-
|
||||||
|
%% If consumed all innput and there's a
|
||||||
|
%% tree, we're done
|
||||||
T \= void,
|
T \= void,
|
||||||
!.
|
!.
|
||||||
|
|
||||||
parse_number(op('.', TL, TR)) -->
|
%% parse_floating_number(-tree, +stream, -not_consumed) is det
|
||||||
parse_number_explicit(void, void, TL),
|
%
|
||||||
|
% Parse a floating point number
|
||||||
|
%
|
||||||
|
parse_floating_number(op('.', TL, TR)) -->
|
||||||
|
%% A float is a node with a dot as the operator
|
||||||
|
%% If there's a number on the left
|
||||||
|
parse_number(TL),
|
||||||
|
%% Followed by either point or dot
|
||||||
[X],
|
[X],
|
||||||
{ member(X, [point, dot]), ! },
|
{ member(X, [point, dot]), ! },
|
||||||
parse_number_explicit(void, void, TR).
|
%% Followed by another number
|
||||||
|
parse_positive_number(TR).
|
||||||
|
|
||||||
|
%% parse_positive_number(-tree, +stream, -not_consumed) is det
|
||||||
|
%
|
||||||
|
% Parse an positive integer number
|
||||||
|
%
|
||||||
|
parse_positive_number(N) -->
|
||||||
|
[N],
|
||||||
|
%% CLPFD, a number between 0 and infinity
|
||||||
|
{ N in 0..sup, ! }.
|
||||||
|
parse_positive_number(T) -->
|
||||||
|
parse_number_explicit(void, void, T).
|
||||||
|
|
||||||
|
%% parse_number(-tree, +stream, -not_consumed) is det
|
||||||
|
%
|
||||||
|
% Parse an integer number
|
||||||
|
%
|
||||||
parse_number(N) -->
|
parse_number(N) -->
|
||||||
[N],
|
[N],
|
||||||
{ number(N), ! }.
|
%% CLPFD, a number between negative infinity and positive infinity
|
||||||
|
{ not(atom(N)), N in inf..sup, ! }.
|
||||||
|
parse_number(op(neg, T)) --> % TODO
|
||||||
|
%% A number can start with negative, to negate it
|
||||||
|
[negative],
|
||||||
|
parse_number_explicit(void, void, T).
|
||||||
parse_number(T) -->
|
parse_number(T) -->
|
||||||
parse_number_explicit(void, void, T).
|
parse_number_explicit(void, void, T).
|
||||||
%% NOTE This is not supposed to be here.
|
|
||||||
%% polynomial_tree_to_polynomial(T1, PP),
|
|
||||||
%% simpoly(PP, T2).
|
|
||||||
%% Tests:
|
%% Tests:
|
||||||
%% ?- parse_number(T, [two], _).
|
%% ?- parse_number(T, [two], _).
|
||||||
%@ T = 2.
|
%@ T = 2.
|
||||||
|
@ -296,7 +407,8 @@ parse_number(T) -->
|
||||||
%@ false.
|
%@ false.
|
||||||
%% ?- parse_number(T, [twenty, one], _).
|
%% ?- parse_number(T, [twenty, one], _).
|
||||||
%@ T = op(+, 20, 1).
|
%@ T = op(+, 20, 1).
|
||||||
%% ?- parse_number(T, [hundred], _).
|
%% ?- parse_number(T, [negative, one, hundred], _).
|
||||||
|
%@ T = op(neg, op(*, 1, 100)) ;
|
||||||
%@ false.
|
%@ false.
|
||||||
%% ?- parse_number(T, [three, hundred], _).
|
%% ?- parse_number(T, [three, hundred], _).
|
||||||
%@ T = op(*, 3, 100).
|
%@ T = op(*, 3, 100).
|
||||||
|
@ -327,7 +439,6 @@ parse_number(T) -->
|
||||||
%
|
%
|
||||||
% Parse powers
|
% Parse powers
|
||||||
%
|
%
|
||||||
%% Order matters
|
|
||||||
parse_power(op(^, TB, 2)) -->
|
parse_power(op(^, TB, 2)) -->
|
||||||
parse_polynomial_variable(TB),
|
parse_polynomial_variable(TB),
|
||||||
[squared].
|
[squared].
|
||||||
|
@ -337,49 +448,94 @@ parse_power(op(^, TB, 3)) -->
|
||||||
parse_power(op(^, TB, TN)) -->
|
parse_power(op(^, TB, TN)) -->
|
||||||
parse_polynomial_variable(TB),
|
parse_polynomial_variable(TB),
|
||||||
[raised, to],
|
[raised, to],
|
||||||
parse_number(TN).
|
parse_positive_number(TN).
|
||||||
parse_power(TB) -->
|
parse_power(TB) -->
|
||||||
parse_polynomial_variable(TB).
|
parse_polynomial_variable(TB).
|
||||||
|
|
||||||
|
%% parse_operation(+Op, +stream, -not_consumed) is det
|
||||||
|
%
|
||||||
|
% Associate an operator with a word
|
||||||
|
%
|
||||||
parse_operation(+) --> [plus].
|
parse_operation(+) --> [plus].
|
||||||
parse_operation(*) --> [times].
|
parse_operation(*) --> [times].
|
||||||
|
|
||||||
parse_polynomial_operand(T) --> parse_number(T).
|
%% parse_polynomial_operand(-tree) is det
|
||||||
parse_polynomial_operand(T) --> parse_power(T).
|
%
|
||||||
parse_polynomial_operand(load(T)) --> parse_stored_variable(T), { ! }.
|
% Parse a valid operand for either side of an
|
||||||
|
% operation in a polynomial
|
||||||
|
%
|
||||||
|
parse_polynomial_operand(T) -->
|
||||||
|
parse_floating_number(T).
|
||||||
|
parse_polynomial_operand(T) -->
|
||||||
|
parse_power(T).
|
||||||
|
parse_polynomial_operand(load(T)) -->
|
||||||
|
%% Tag the variable, to be loaded later
|
||||||
|
parse_stored_variable(T),
|
||||||
|
{ ! }.
|
||||||
|
|
||||||
|
%% Declare polynomial_store as a dynamic predicate with two arguments
|
||||||
|
%% Used to store and retrieve polynomials associated with a variable
|
||||||
:- dynamic polynomial_store/2.
|
:- dynamic polynomial_store/2.
|
||||||
|
|
||||||
|
%% parse_stored_variable(-var) is det
|
||||||
|
%
|
||||||
|
% Parse a valid variable name, for storage
|
||||||
|
% A valid varaible is one that would be valid in Prolog
|
||||||
|
%
|
||||||
parse_stored_variable(P) -->
|
parse_stored_variable(P) -->
|
||||||
[P],
|
[P],
|
||||||
{
|
{ valid_store_variable(P) }.
|
||||||
atom_codes(P, L),
|
|
||||||
cons(F, R, L),
|
|
||||||
code_type(F, prolog_var_start),
|
|
||||||
maplist(code_type_swap(prolog_identifier_continue), R)
|
|
||||||
}.
|
|
||||||
%% Tests:
|
%% Tests:
|
||||||
%% ?- parse_stored_variable(P, ['P1'], _).
|
%% ?- parse_stored_variable(P, ['P1'], _).
|
||||||
%@ P = 'P1'.
|
%@ P = 'P1'.
|
||||||
%% ?- parse_stored_variable(P, ['x1'], _).
|
%% ?- parse_stored_variable(P, ['x1'], _).
|
||||||
%@ false.
|
%@ false.
|
||||||
|
|
||||||
|
%% parse_stored_variable(+var) is det
|
||||||
|
%
|
||||||
|
% Check if var is a valid variable name, for storage
|
||||||
|
% A valid varaible is one that would be valid in Prolog
|
||||||
|
%
|
||||||
|
valid_store_variable(P) :-
|
||||||
|
%% Get the list of codes of the atom
|
||||||
|
atom_codes(P, L),
|
||||||
|
%% Extract the first element
|
||||||
|
cons(F, R, L),
|
||||||
|
%% Partially builtin facilities:
|
||||||
|
%% Check if it's a valid start to a Prolog variable
|
||||||
|
code_type(F, prolog_var_start),
|
||||||
|
%% Check that each of the following is a valid
|
||||||
|
%% continuation to a variable name
|
||||||
|
maplist(code_type_swap(prolog_identifier_continue), R).
|
||||||
|
|
||||||
|
%% code_type_swap - swap arguments of code_type, so
|
||||||
|
%% it can be used with maplist. No lambdas...
|
||||||
code_type_swap(X, Y) :- code_type(Y, X).
|
code_type_swap(X, Y) :- code_type(Y, X).
|
||||||
|
|
||||||
|
%% parse_polynomial_variable(-base, +stream, -not_consumed) is det
|
||||||
|
%
|
||||||
|
% Parse a polynomial variable
|
||||||
|
%
|
||||||
parse_polynomial_variable(B) -->
|
parse_polynomial_variable(B) -->
|
||||||
[B],
|
[B],
|
||||||
{ polynomial_variable(B) }.
|
{ polynomial_variable(B) }.
|
||||||
|
|
||||||
|
%% parse_polynomial(-tree, +stream, -not_consumed) is det
|
||||||
|
%
|
||||||
|
% Parse a polynomial. Delegates to explicit variant
|
||||||
|
%
|
||||||
parse_polynomial(T) -->
|
parse_polynomial(T) -->
|
||||||
|
%% Ignore "polynomail", if followed by a valid polynomial
|
||||||
[polynomial],
|
[polynomial],
|
||||||
{ ! },
|
{ ! },
|
||||||
|
%% Delegate
|
||||||
parse_polynomial_explicit(_-_, T).
|
parse_polynomial_explicit(_-_, T).
|
||||||
parse_polynomial(T, NC, NC) :-
|
parse_polynomial(void, NC, NC) :-
|
||||||
not(parse_polynomial_explicit(_-_, T, NC, _)),
|
%% If can't parse more, done here
|
||||||
|
not(parse_polynomial_explicit(_-_, _, NC, _)),
|
||||||
!.
|
!.
|
||||||
parse_polynomial(T) -->
|
parse_polynomial(T) -->
|
||||||
|
%% Delegate
|
||||||
parse_polynomial_explicit(_-_, T),
|
parse_polynomial_explicit(_-_, T),
|
||||||
!.
|
!.
|
||||||
%% Tests:
|
%% Tests:
|
||||||
|
@ -395,8 +551,8 @@ parse_polynomial(T) -->
|
||||||
%@ T = op(+, 2, op(*, 3, 4)).
|
%@ T = op(+, 2, op(*, 3, 4)).
|
||||||
%% ?- parse_polynomial(T, [two, plus, three, times, four, plus, six, times, five], _).
|
%% ?- parse_polynomial(T, [two, plus, three, times, four, plus, six, times, five], _).
|
||||||
%@ T = op(+, 2, op(+, op(*, 3, 4), op(*, 6, 5))).
|
%@ T = op(+, 2, op(+, op(*, 3, 4), op(*, 6, 5))).
|
||||||
%% ?- parse_polynomial(T, [two, times, times, two], NC), write(T).
|
%% ?- parse_polynomial(T, [two, times, times, two], NC).
|
||||||
%@ _2986 %% NOTE Potential problem. It seems NC isn't unified with the list, if it fails
|
%@ T = void,
|
||||||
%@ NC = [two, times, times, two].
|
%@ NC = [two, times, times, two].
|
||||||
%% ?- parse_polynomial(T, [two, plus, x, times, four], _).
|
%% ?- parse_polynomial(T, [two, plus, x, times, four], _).
|
||||||
%@ T = op(+, 2, op(*, x, 4)).
|
%@ T = op(+, 2, op(*, x, 4)).
|
||||||
|
@ -409,36 +565,69 @@ parse_polynomial(T) -->
|
||||||
%@ T = op(+, op(+, 2, 3), op(*, 4, y)),
|
%@ T = op(+, op(+, 2, 3), op(*, 4, y)),
|
||||||
%@ NC = [].
|
%@ NC = [].
|
||||||
|
|
||||||
|
%% parse_polynomial_explicit(+tree-at, -tree) is det
|
||||||
|
%
|
||||||
|
% Explicitly parse a polynomial. You probably don't want
|
||||||
|
% to call this directly. The first argument is a difference
|
||||||
|
% structure of the tree on the left; the second of the pair
|
||||||
|
% represents where to put the rest of the tree, in
|
||||||
|
% subsequent passes
|
||||||
|
%
|
||||||
|
% Call with _-_ on the first argument, outputs the
|
||||||
|
% tree on the right
|
||||||
|
%
|
||||||
parse_polynomial_explicit(void-_, T) -->
|
parse_polynomial_explicit(void-_, T) -->
|
||||||
|
%% Entry point. If the three on the left is void
|
||||||
|
%% Parse an operand and an op
|
||||||
parse_polynomial_operand(TL),
|
parse_polynomial_operand(TL),
|
||||||
parse_operation(Op),
|
parse_operation(Op),
|
||||||
|
%% If consumed an op, double down and don't go back
|
||||||
!,
|
!,
|
||||||
|
%% Recurse with the tree on the left populated with
|
||||||
|
%% what we found and a reference to the place to
|
||||||
|
%% place the next tree
|
||||||
parse_polynomial_explicit(op(Op, TL, TRP)-TRP, T).
|
parse_polynomial_explicit(op(Op, TL, TRP)-TRP, T).
|
||||||
parse_polynomial_explicit(TLP-TL, T) -->
|
parse_polynomial_explicit(TLP-TL, T) -->
|
||||||
|
%% Parse an operand on the left and place it in the tree
|
||||||
|
%% on the left, through the difference structure,
|
||||||
parse_polynomial_operand(TL),
|
parse_polynomial_operand(TL),
|
||||||
|
%% if we find a plus
|
||||||
parse_operation(+),
|
parse_operation(+),
|
||||||
!,
|
!,
|
||||||
|
%% Recurse on the right; position the sub tree on the right
|
||||||
parse_polynomial_explicit(op(+, TLP, TRP)-TRP, T).
|
parse_polynomial_explicit(op(+, TLP, TRP)-TRP, T).
|
||||||
parse_polynomial_explicit(TLP-T, TLP) -->
|
parse_polynomial_explicit(TLP-T, TLP) -->
|
||||||
|
%% Parse an operand on the left, placing the result of the
|
||||||
|
%% recusion on the TLP, through the difference structure and return that
|
||||||
parse_polynomial_operand(TL),
|
parse_polynomial_operand(TL),
|
||||||
|
%% If we find a times,
|
||||||
parse_operation(*),
|
parse_operation(*),
|
||||||
!,
|
!,
|
||||||
|
%% Place the operand on the left, the following on the
|
||||||
|
%% right and get a T to place in the tree on the left from above
|
||||||
parse_polynomial_explicit(op(*, TL, TRP)-TRP, T).
|
parse_polynomial_explicit(op(*, TL, TRP)-TRP, T).
|
||||||
parse_polynomial_explicit(TLP-T, TLP) -->
|
parse_polynomial_explicit(TLP-T, TLP) -->
|
||||||
|
%% Same as above, but without an operator,
|
||||||
parse_polynomial_operand(TL),
|
parse_polynomial_operand(TL),
|
||||||
|
%% Assume times
|
||||||
parse_polynomial_explicit(op(*, TL, TRP)-TRP, T),
|
parse_polynomial_explicit(op(*, TL, TRP)-TRP, T),
|
||||||
!.
|
!.
|
||||||
parse_polynomial_explicit(TLP-TL, TLP) -->
|
parse_polynomial_explicit(TLP-TL, TLP) -->
|
||||||
{ TLP \= void },
|
{ TLP \= void },
|
||||||
|
%% Base case. Parse a last operand
|
||||||
parse_polynomial_operand(TL),
|
parse_polynomial_operand(TL),
|
||||||
!,
|
!,
|
||||||
{ TL \= void }.
|
{ TL \= void }.
|
||||||
parse_polynomial_explicit(void-_, T) -->
|
parse_polynomial_explicit(void-_, T) -->
|
||||||
|
%% Base case. Parse a single operand
|
||||||
parse_polynomial_operand(T),
|
parse_polynomial_operand(T),
|
||||||
!,
|
!,
|
||||||
{ T \= void }.
|
{ T \= void }.
|
||||||
|
|
||||||
|
%% parse_command(-tree, +stream, -not_consumed) is semidet
|
||||||
|
%
|
||||||
|
% Parse each individual command
|
||||||
|
%
|
||||||
parse_command(show_stored_polynomials) -->
|
parse_command(show_stored_polynomials) -->
|
||||||
[show, stored, polynomials].
|
[show, stored, polynomials].
|
||||||
parse_command(forget(P)) -->
|
parse_command(forget(P)) -->
|
||||||
|
@ -485,16 +674,28 @@ parse_command(op(+, TN, TP)) -->
|
||||||
%@ T = show(void, 3),
|
%@ T = show(void, 3),
|
||||||
%@ NC = [] .
|
%@ NC = [] .
|
||||||
%% ?- parse_command(T, [add, 3, plus, x, to, 4, plus, x], NC).
|
%% ?- parse_command(T, [add, 3, plus, x, to, 4, plus, x], NC).
|
||||||
|
%@ false.
|
||||||
%@ T = op(+, op(+, 3, x), op(+, 4, x)),
|
%@ T = op(+, op(+, 3, x), op(+, 4, x)),
|
||||||
%@ NC = [] ;
|
%@ NC = [] ;
|
||||||
%@ false.
|
%@ false.
|
||||||
|
|
||||||
|
%% parse_input(-tree) is det
|
||||||
|
%
|
||||||
|
% Parse each command and string it into a list
|
||||||
|
%
|
||||||
parse_input(command(TCL, TCR)) -->
|
parse_input(command(TCL, TCR)) -->
|
||||||
|
%% Result is a struct with a command on the left
|
||||||
|
%% and a struct of commands on the right
|
||||||
|
%% parse a single command
|
||||||
parse_command(TCL),
|
parse_command(TCL),
|
||||||
|
%% separator
|
||||||
[and],
|
[and],
|
||||||
!,
|
!,
|
||||||
|
%% Recurse
|
||||||
parse_input(TCR).
|
parse_input(TCR).
|
||||||
parse_input(command(TC, void)) -->
|
parse_input(command(TC, void)) -->
|
||||||
|
%% If there's no separator, we're done recursing
|
||||||
|
%% parse a single command
|
||||||
parse_command(TC).
|
parse_command(TC).
|
||||||
%% ?- parse_input(CT, [show, 3], _).
|
%% ?- parse_input(CT, [show, 3], _).
|
||||||
%@ CT = command(show(void, 3), void).
|
%@ CT = command(show(void, 3), void).
|
||||||
|
|
Reference in New Issue