The Great Documentation Efort, a small refactor and small bug fixes

This commit is contained in:
Hugo Sales 2018-12-20 02:24:31 +00:00
parent b4a94461a7
commit dfdd65abdf
1 changed files with 298 additions and 97 deletions

View File

@ -46,52 +46,66 @@ polyplay :-
prompt(OldPrompt, '> '),
%% Read a line as codes, from the 'user_input' stream
read_line_to_codes(user_input, InCodes),
%% Restore old prompt
prompt(_, OldPrompt),
%% Split the input at spaces and ignore \r and \t
split_string(InCodes, " ", "\r\t", LS),
%% Convert each set of codes into a term or atom, as appropriate
maplist(name, LA, LS),
(
%% If we read a 'bye', terminate
LA == [bye],
write("See ya"),
nl,
!
;
(
%% Parse the input into a tree
parse_input(TIn, LA, NC),
(
parse_input(TIn, LA, NC),
(
TIn == void,
writeln("I didn't understand what you want."),
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)
)
)
)
%% If the tree is empty, it means nothing was understood
TIn == void,
writeln("I didn't understand what you want."),
writeln(NC)
;
writeln("I didn't understand what you want.")
),
polyplay
(
%% If there is unconsumed input, it means there was a syntatic error
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
debug_print(false).
process_input(command(CL, void)) :-
do_process_input(CL).
%% process_input(+tree) is determines
%
% Execute the commands from the parse tree
%
process_input(command(CL, TCR)) :-
%% Process first command
do_process_input(CL),
@ -99,98 +113,147 @@ process_input(command(CL, TCR)) :-
TCR \== void,
%% recurse
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) :-
%% 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),
%% Print the list of stored variables
print_all_stored_variables(D).
do_process_input(show(store(P), T)) :-
( polynomial_store(P, _),
retractall(polynomial_store(P, _)); true ),
!,
assertz(polynomial_store(P, T)),
write(P),
write(" = "),
polynomial_tree_to_polynomial(T, Pl),
write(Pl),
nl.
%% If the command was to store and show
%% Delegate the work of storing
do_process_input(store(P, T)),
%% Show the variable and the polynomial. Delegate
do_process_input(show(P, T)).
do_process_input(show(load(P), void)) :-
%% Make sure there's a variable
P \== void,
(
%% Check if the variable is stored
polynomial_store(P, T),
write(P),
write(" = "),
polynomial_tree_to_polynomial(T, Pl),
write(Pl),
nl
%% Show the variable and the polynomial. Delegate
do_process_input(show(P, T))
;
write("Variable not stored"),
nl
writeln("Variable not stored")
).
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)) :-
%% Make sure the arguments are in the right format and not one
%% that should be handled by other clauses
P \== void,
P \== store(_),
P \== load(_),
T \== void,
%% Write out the variable and the normal
%% representation of the polynomial
write(P),
write(" = "),
%% Convert the input tree to a flat polynomial, to print
polynomial_tree_to_polynomial(T, Pl),
write(Pl),
nl.
do_process_input(show(void, T)) :-
T \== void,
polynomial_tree_to_polynomial(T, Pl),
write(Pl),
nl.
writeln(Pl).
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)).
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)) :-
%% Convert the polynomial to it's flat representation
polynomial_tree_to_polynomial(PT, P),
%% Use simpoly from the UI layer to simplify, so it
%% gives nice error messages
simpoly(P, SP),
write(SP),
nl.
writeln(SP).
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(PT, P),
%% Use function from UI layer
scalepoly(N, P, P2),
%% Simplify
simpoly(P2, SP),
write(SP),
nl.
writeln(SP).
%% print_all_stored_variables
%
% Prints the stored variables
% Prints the stored variables, given the list
%
print_all_stored_variables([nm(X,T) | TS]) :-
%% Print
write(X),
write(" = "),
polynomial_tree_to_polynomial(T, P),
write(P),
nl,
writeln(P),
%% Recurse on the right
print_all_stored_variables(TS).
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) :-
%% 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(TR, PR),
%% Convert the results of the recursion to atoms
term_to_atom(PL, TermL),
term_to_atom(PR, TermR),
%% Concat the parts
atom_concat(TermL, Op, Temp),
atom_concat(Temp, TermR, PA),
%% Convert back to terms
term_to_atom(P, PA).
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),
%% Recurse as if there was a normal tree here
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).
%% 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
% 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(a, 1, f).
@ -228,58 +291,106 @@ special_word_number(million, 1000000, xfy).
%% special_word_number(third, 0.33333, xf).
%% special_word_number(quarter, 0.25, xf).
%% nlp_number(?W:Atom, ?D:Int) is det
%% parse_number_explicit(+precedence, +tree_left, -tree,
%% +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) :-
%% Entry point
%% Convert a word to a number with precedence f, g, or fy
special_word_number(WN, N, P),
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(fy, NL, T, [WN | In], NC) :-
%% If we read an fy before and now an 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(xfy, TL, T, [WN | In], NC) :-
%% Parsed an xfy before, now parse any number on the right
TL \= void,
special_word_number(WN, N, P),
member(P, [f, g, fy]),
!,
%% Add the parts
parse_number_explicit(P, op(+, TL, N), T, In, NC).
parse_number_explicit(_, TL, T, [WN | In], NC) :-
%% Whatever the precedence on the left
special_word_number(WN, N, xfy),
%% Enforce there was a left argument
TL \= void,
!,
%% Multiply this and the left
parse_number_explicit(xfy, op(*, TL, N), T, 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, _, _),
%% Passthrough
parse_number_explicit(P, TL, T, [WN | In], NC),
!.
parse_number_explicit(_, T, T, [WN | In], [WN | In]) :-
%% If there's a tree
T \= void,
%% And can't consume more words as
%% numbers (fail on first), then we're done
not(special_word_number(WN, _, _)),
!.
parse_number_explicit(_, T, T, [], []) :-
%% If consumed all innput and there's a
%% tree, we're done
T \= void,
!.
parse_number(op('.', TL, TR)) -->
parse_number_explicit(void, void, TL),
%% parse_floating_number(-tree, +stream, -not_consumed) is det
%
% 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],
{ 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) -->
[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_explicit(void, void, T).
%% NOTE This is not supposed to be here.
%% polynomial_tree_to_polynomial(T1, PP),
%% simpoly(PP, T2).
%% Tests:
%% ?- parse_number(T, [two], _).
%@ T = 2.
@ -296,7 +407,8 @@ parse_number(T) -->
%@ false.
%% ?- parse_number(T, [twenty, one], _).
%@ T = op(+, 20, 1).
%% ?- parse_number(T, [hundred], _).
%% ?- parse_number(T, [negative, one, hundred], _).
%@ T = op(neg, op(*, 1, 100)) ;
%@ false.
%% ?- parse_number(T, [three, hundred], _).
%@ T = op(*, 3, 100).
@ -327,7 +439,6 @@ parse_number(T) -->
%
% Parse powers
%
%% Order matters
parse_power(op(^, TB, 2)) -->
parse_polynomial_variable(TB),
[squared].
@ -337,49 +448,94 @@ parse_power(op(^, TB, 3)) -->
parse_power(op(^, TB, TN)) -->
parse_polynomial_variable(TB),
[raised, to],
parse_number(TN).
parse_positive_number(TN).
parse_power(TB) -->
parse_polynomial_variable(TB).
%% parse_operation(+Op, +stream, -not_consumed) is det
%
% Associate an operator with a word
%
parse_operation(+) --> [plus].
parse_operation(*) --> [times].
parse_polynomial_operand(T) --> parse_number(T).
parse_polynomial_operand(T) --> parse_power(T).
parse_polynomial_operand(load(T)) --> parse_stored_variable(T), { ! }.
%% parse_polynomial_operand(-tree) is det
%
% 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.
%% 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) -->
[P],
{
atom_codes(P, L),
cons(F, R, L),
code_type(F, prolog_var_start),
maplist(code_type_swap(prolog_identifier_continue), R)
}.
{ valid_store_variable(P) }.
%% Tests:
%% ?- parse_stored_variable(P, ['P1'], _).
%@ P = 'P1'.
%% ?- parse_stored_variable(P, ['x1'], _).
%@ 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).
%% parse_polynomial_variable(-base, +stream, -not_consumed) is det
%
% Parse a polynomial variable
%
parse_polynomial_variable(B) -->
[B],
{ polynomial_variable(B) }.
%% parse_polynomial(-tree, +stream, -not_consumed) is det
%
% Parse a polynomial. Delegates to explicit variant
%
parse_polynomial(T) -->
%% Ignore "polynomail", if followed by a valid polynomial
[polynomial],
{ ! },
%% Delegate
parse_polynomial_explicit(_-_, T).
parse_polynomial(T, NC, NC) :-
not(parse_polynomial_explicit(_-_, T, NC, _)),
parse_polynomial(void, NC, NC) :-
%% If can't parse more, done here
not(parse_polynomial_explicit(_-_, _, NC, _)),
!.
parse_polynomial(T) -->
%% Delegate
parse_polynomial_explicit(_-_, T),
!.
%% Tests:
@ -395,8 +551,8 @@ parse_polynomial(T) -->
%@ T = op(+, 2, op(*, 3, 4)).
%% ?- parse_polynomial(T, [two, plus, three, times, four, plus, six, times, five], _).
%@ T = op(+, 2, op(+, op(*, 3, 4), op(*, 6, 5))).
%% ?- parse_polynomial(T, [two, times, times, two], NC), write(T).
%@ _2986 %% NOTE Potential problem. It seems NC isn't unified with the list, if it fails
%% ?- parse_polynomial(T, [two, times, times, two], NC).
%@ T = void,
%@ NC = [two, times, times, two].
%% ?- parse_polynomial(T, [two, plus, x, times, four], _).
%@ T = op(+, 2, op(*, x, 4)).
@ -409,36 +565,69 @@ parse_polynomial(T) -->
%@ T = op(+, op(+, 2, 3), op(*, 4, y)),
%@ 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) -->
%% Entry point. If the three on the left is void
%% Parse an operand and an op
parse_polynomial_operand(TL),
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(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),
%% if we find a plus
parse_operation(+),
!,
%% Recurse on the right; position the sub tree on the right
parse_polynomial_explicit(op(+, TLP, TRP)-TRP, T).
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),
%% If we find a times,
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(TLP-T, TLP) -->
%% Same as above, but without an operator,
parse_polynomial_operand(TL),
%% Assume times
parse_polynomial_explicit(op(*, TL, TRP)-TRP, T),
!.
parse_polynomial_explicit(TLP-TL, TLP) -->
{ TLP \= void },
%% Base case. Parse a last operand
parse_polynomial_operand(TL),
!,
{ TL \= void }.
parse_polynomial_explicit(void-_, T) -->
%% Base case. Parse a single operand
parse_polynomial_operand(T),
!,
{ T \= void }.
%% parse_command(-tree, +stream, -not_consumed) is semidet
%
% Parse each individual command
%
parse_command(show_stored_polynomials) -->
[show, stored, polynomials].
parse_command(forget(P)) -->
@ -485,16 +674,28 @@ parse_command(op(+, TN, TP)) -->
%@ T = show(void, 3),
%@ NC = [] .
%% ?- parse_command(T, [add, 3, plus, x, to, 4, plus, x], NC).
%@ false.
%@ T = op(+, op(+, 3, x), op(+, 4, x)),
%@ NC = [] ;
%@ false.
%% parse_input(-tree) is det
%
% Parse each command and string it into a list
%
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),
%% separator
[and],
!,
%% Recurse
parse_input(TCR).
parse_input(command(TC, void)) -->
%% If there's no separator, we're done recursing
%% parse a single command
parse_command(TC).
%% ?- parse_input(CT, [show, 3], _).
%@ CT = command(show(void, 3), void).