mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
library/ops.m:
Instead of an op_table mapping a string to a list of one or more op_infos,
make it map the string to a single structure, the op_infos, which has
four slots, one each for infix, binary prefix, prefix and postfix
op information. This allows parsers and unparsers to go directly to
the kind of operator (e.g. infix or prefix) that they are interested in.
NEWS:
Announce the change.
compiler/parse_tree_out_term.m:
library/mercury_term_parser.m:
library/pretty_printer.m:
library/stream.string_writer.m:
library/string.to_string.m:
samples/calculator2.m:
tests/hard_coded/bug383.m:
Conform to the change. In several places, the new operator representation
allows the replacement of loops with direct lookups, and the replacement
of if-then-else chains with switches.
Add reminders about keeping two predicates in sync.
254 lines
7.6 KiB
Mathematica
254 lines
7.6 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Another calculator - parses and evaluates integer expression terms.
|
|
% This module demonstrates the use of user-defined operator precedence
|
|
% tables with mercury_term_parser.read_term.
|
|
%
|
|
% Note that unlike calculator.m, the expressions must be terminated with a `.'.
|
|
% This version also allows variable assignments of the form `X = Exp.'.
|
|
%
|
|
% Author: stayl.
|
|
%
|
|
% This source file is hereby placed in the public domain. -stayl.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module calculator2.
|
|
:- interface.
|
|
|
|
:- import_module io.
|
|
|
|
:- pred main(io::di, io::uo) is cc_multi.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module exception.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module mercury_term_parser.
|
|
:- import_module ops.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module term_int.
|
|
:- import_module term_io.
|
|
:- import_module univ.
|
|
:- import_module varset.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type calc_info == map(string, int).
|
|
|
|
main(!IO) :-
|
|
main_loop(map.init, !IO).
|
|
|
|
:- pred main_loop(calc_info::in, io::di, io::uo) is cc_multi.
|
|
|
|
main_loop(!.CalcInfo, !IO) :-
|
|
io.write_string("calculator> ", !IO),
|
|
io.flush_output(!IO),
|
|
mercury_term_parser.read_term_with_op_table(calculator_op_table, Res, !IO),
|
|
(
|
|
Res = eof,
|
|
io.write_string("EOF\n", !IO)
|
|
;
|
|
Res = error(Msg, _Line),
|
|
io.format("%s\n", [s(Msg)], !IO),
|
|
main_loop(!.CalcInfo, !IO)
|
|
;
|
|
Res = term(VarSet, Term),
|
|
( if
|
|
Term = term.functor(term.atom("="),
|
|
[term.variable(Var, _Context), ExprTerm0], _)
|
|
then
|
|
ExprTerm = ExprTerm0,
|
|
varset.lookup_name(VarSet, Var, VarName),
|
|
SetVar = yes(VarName)
|
|
else
|
|
ExprTerm = Term,
|
|
SetVar = no
|
|
),
|
|
|
|
try(
|
|
( pred(Num0::out) is det :-
|
|
Num0 = eval_expr(!.CalcInfo, VarSet, ExprTerm)
|
|
), EvalResult),
|
|
(
|
|
EvalResult = succeeded(Num),
|
|
io.format("%d\n", [i(Num)], !IO),
|
|
(
|
|
SetVar = yes(VarToSet),
|
|
map.set(VarToSet, Num, !CalcInfo)
|
|
;
|
|
SetVar = no
|
|
)
|
|
;
|
|
EvalResult = exception(Exception),
|
|
( if univ_to_type(Exception, EvalError) then
|
|
report_eval_error(EvalError, !IO)
|
|
else
|
|
rethrow(EvalResult)
|
|
)
|
|
),
|
|
|
|
% Recursively call ourself for the next term(s).
|
|
main_loop(!.CalcInfo, !IO)
|
|
).
|
|
|
|
:- pred report_eval_error(eval_error::in, io::di, io::uo) is det.
|
|
|
|
report_eval_error(Error, !IO) :-
|
|
(
|
|
Error = unknown_operator(Name, Arity),
|
|
io.format("unknown operator `%s/%d'.\n", [s(Name), i(Arity)], !IO)
|
|
;
|
|
Error = unknown_variable(Name),
|
|
io.format("unknown variable `%s'.\n", [s(Name)], !IO)
|
|
;
|
|
Error = unexpected_const(Const),
|
|
io.write_string("unexpected ", !IO),
|
|
(
|
|
Const = term.float(Float),
|
|
io.format("unexpected float `%f'\n", [f(Float)], !IO)
|
|
;
|
|
Const = term.string(String),
|
|
io.format("unexpected string ""%s""\n", [s(String)], !IO)
|
|
;
|
|
( Const = term.integer(_, _, _, _)
|
|
; Const = term.atom(_)
|
|
; Const = term.implementation_defined(_)
|
|
),
|
|
error("report_eval_error")
|
|
)
|
|
).
|
|
|
|
:- func eval_expr(calc_info, varset, term) = int.
|
|
|
|
eval_expr(CalcInfo, VarSet, Term) = Res :-
|
|
(
|
|
Term = term.variable(Var, _Context),
|
|
varset.lookup_name(VarSet, Var, VarName),
|
|
( if map.search(CalcInfo, VarName, Res0) then
|
|
Res = Res0
|
|
else
|
|
throw(unknown_variable(VarName))
|
|
)
|
|
;
|
|
Term = term.functor(term.atom(Op), Args, _),
|
|
( if
|
|
(
|
|
Args = [Arg1],
|
|
Res0 = eval_unop(Op, eval_expr(CalcInfo, VarSet, Arg1))
|
|
;
|
|
Args = [Arg1, Arg2],
|
|
Res0 = eval_binop(Op,
|
|
eval_expr(CalcInfo, VarSet, Arg1),
|
|
eval_expr(CalcInfo, VarSet, Arg2))
|
|
)
|
|
then
|
|
Res = Res0
|
|
else
|
|
throw(unknown_operator(Op, list.length(Args)))
|
|
)
|
|
;
|
|
Term = term.functor(Const, _, Context),
|
|
Const = term.integer(_, _, _, _),
|
|
( if term_int.term_to_int(Term, Int) then
|
|
Res = Int
|
|
else
|
|
throw(unexpected_const(Const) - Context)
|
|
)
|
|
;
|
|
Term = term.functor(term.float(Float), _, Context),
|
|
throw(unexpected_const(term.float(Float)) - Context)
|
|
;
|
|
Term = term.functor(term.string(String), _, Context),
|
|
throw(unexpected_const(term.string(String)) - Context)
|
|
;
|
|
Term = term.functor(ImplDefConst, _, Context),
|
|
ImplDefConst = term.implementation_defined(_),
|
|
throw(unexpected_const(ImplDefConst) - Context)
|
|
).
|
|
|
|
:- func eval_unop(string, int) = int is semidet.
|
|
|
|
eval_unop("-", Num) = -Num.
|
|
eval_unop("+", Num) = Num.
|
|
|
|
:- func eval_binop(string, int, int) = int is semidet.
|
|
|
|
eval_binop("-", Num1, Num2) = Num1 - Num2.
|
|
eval_binop("+", Num1, Num2) = Num1 + Num2.
|
|
eval_binop("*", Num1, Num2) = Num1 * Num2.
|
|
eval_binop("//", Num1, Num2) = Num1 // Num2.
|
|
|
|
:- type eval_error
|
|
---> unknown_operator(
|
|
string, % name
|
|
int % arity
|
|
)
|
|
; unknown_variable(string)
|
|
; unexpected_const(term.const).
|
|
|
|
:- type calculator_op_table
|
|
---> calculator_op_table.
|
|
|
|
:- pred ops_table(string::in, op_infos::out) is semidet.
|
|
|
|
ops_table(Op, OpInfos) :-
|
|
(
|
|
(Op = "//" ; Op = "*"),
|
|
Infix = in(prio(1100u), arg_gt, arg_ge),
|
|
OpInfos = op_infos(Infix, no_bin_pre, no_pre, no_post)
|
|
;
|
|
(Op = "+" ; Op = "-"),
|
|
Infix = in(prio(1000u), arg_gt, arg_ge),
|
|
Prefix = pre(prio(1000u), arg_ge),
|
|
OpInfos = op_infos(Infix, no_bin_pre, Prefix, no_post)
|
|
;
|
|
Op = "=",
|
|
Infix = in(prio(800u), arg_ge, arg_ge),
|
|
OpInfos = op_infos(Infix, no_bin_pre, no_pre, no_post)
|
|
).
|
|
|
|
:- instance ops.op_table(calculator_op_table) where [
|
|
( ops.lookup_infix_op(_, Op, Priority, GtOrGeA, GtOrGeB) :-
|
|
calculator2.ops_table(Op, OpInfos),
|
|
OpInfos ^ oi_infix = in(Priority, GtOrGeA, GtOrGeB)
|
|
),
|
|
|
|
( ops.lookup_prefix_op(_, Op, Priority, GtOrGeA) :-
|
|
calculator2.ops_table(Op, OpInfos),
|
|
OpInfos ^ oi_prefix = pre(Priority, GtOrGeA)
|
|
),
|
|
|
|
ops.lookup_postfix_op(_, _, _, _) :- fail,
|
|
ops.lookup_binary_prefix_op(_, _, _, _, _) :- fail,
|
|
|
|
ops.is_op(_Table, Op) :-
|
|
ops_table(Op, _OpInfos),
|
|
|
|
ops.lookup_op_infos(_, Op, OpInfos) :-
|
|
ops_table(Op, OpInfos),
|
|
|
|
ops.lookup_operator_term(_, _, _, _) :- fail,
|
|
|
|
ops.tightest_op_priority(_) = prio(1300u),
|
|
ops.loosest_op_priority(_) = prio(800u),
|
|
ops.universal_priority(_) = prio(799u),
|
|
ops.comma_priority(_) = prio(800u),
|
|
ops.arg_priority(_) = prio(799u)
|
|
].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module calculator2.
|
|
%-----------------------------------------------------------------------------%
|