diff --git a/polymani.pl b/polymani.pl index e8c0fea..a48ded6 100644 --- a/polymani.pl +++ b/polymani.pl @@ -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).