Files
mercury/samples/calculator2.m
Julien Fischer e6e295a3cc Generalise the representation of integers in the term module.
In preparation for supporting uint literals and literals for the fixed size
integer types, generalise the representation of integers in the term module, so
that for every integer literal we record its base, value (as an arbitrary
precision integer), signedness and size (the latter two based on the literal's
suffix or lack thereof).

Have the lexer attach information about the integer base to machine sized ints;
we already did this for the 'big_integer' alternative but not the normal one.
In conjunction with the first change, this fixes a problem where the compiler
was accepting non-decimal integers in like arity specifications.  (The
resulting error messages could be improved, but that's a separate change.)

Support uints in more places; mark other places which require further work with
XXX UINT.

library/term.m:
     Generalise the representation of integer terms so that we can store
     the base, signedness and size of a integer along with its value.
     In the new design the value is always stored as an arbitrary precision
     integer so we no longer require the big_integer/2 alternative; delete it.

     Add some utility predicates that make it easier to work with integer terms.

library/term_conversion.m:
library/term_io.m:
     Conform to the above changes,

     Add missing handling for uints in some spots; add XXX UINT comments
     in others -- these will be addressed later.

library/lexer.m:
     Record the base of word sized integer literals.

library/parser.m:
compiler/analysis_file.m:
compiler/fact_table.m:
compiler/get_dependencies.m:
compiler/hlds_out_goal.m:
compiler/hlds_out_util.m:
compiler/intermod.m:
compiler/make.module_dep_file.m:
compiler/parse_class.m:
compiler/parse_inst_mode_name.m:
compiler/parse_item.m:
compiler/parse_pragma.m:
compiler/parse_sym_name.m:
compiler/parse_tree_out_term.m:
compiler/parse_tree_to_term.m:
compiler/parse_type_defn.m:
compiler/parse_util.m:
compiler/prog_ctgc.m:
compiler/prog_util.m:
compiler/recompilation.check.m:
compiler/recompilation.version.m:
compiler/superhomogeneous.m:
mdbcomp/trace_counts.m:
samples/calculator2.m:
extras/moose/moose.m:
    Conform to the above changes.

tests/hard_coded/impl_def_lex.exp:
tests/hard_coded/impl_def_lex_string.exp:
tests/hard_coded/lexer_bigint.exp*:
tests/hard_coded/lexer_zero.exp*:
tests/hard_coded/parse_number_from_string.exp*:
tests/hard_coded/term_to_unit_test.exp:
    Update these expected outputs.
2017-04-22 11:53:14 +10:00

247 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 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 ops.
:- import_module pair.
:- import_module parser.
:- import_module require.
:- import_module string.
:- import_module term.
:- import_module term_io.
:- import_module univ.
:- import_module varset.
%-----------------------------------------------------------------------------%
:- type calc_info == map(string, int).
main(!IO) :-
main_2(map.init, !IO).
:- pred main_2(calc_info::in, io::di, io::uo) is cc_multi.
main_2(CalcInfo0, !IO) :-
io.write_string("calculator> ", !IO),
io.flush_output(!IO),
parser.read_term_with_op_table(calculator_op_table, Res, !IO),
(
Res = error(Msg, _Line),
io.write_string(Msg, !IO),
io.nl(!IO),
main(!IO)
;
Res = eof,
io.write_string("EOF\n", !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(CalcInfo0, VarSet, ExprTerm)
), EvalResult),
(
EvalResult = succeeded(Num),
io.write_int(Num, !IO),
io.nl(!IO),
( SetVar = yes(VarToSet) ->
map.set(VarToSet, Num, CalcInfo0, CalcInfo)
;
CalcInfo = CalcInfo0
)
;
EvalResult = exception(Exception),
CalcInfo = CalcInfo0,
( univ_to_type(Exception, EvalError) ->
report_eval_error(EvalError, !IO)
;
rethrow(EvalResult)
)
),
% Recursively call ourself for the next term(s).
main_2(CalcInfo, !IO)
).
:- pred report_eval_error(eval_error::in, io::di, io::uo) is det.
report_eval_error(unknown_operator(Name, Arity), !IO) :-
io.write_string("unknown operator `", !IO),
io.write_string(Name, !IO),
io.write_string("/", !IO),
io.write_int(Arity, !IO),
io.write_string("'.\n", !IO).
report_eval_error(unknown_variable(Name), !IO) :-
io.write_string("unknown variable `", !IO),
io.write_string(Name, !IO),
io.write_string("'.\n", !IO).
report_eval_error(unexpected_const(Const), !IO) :-
io.write_string("unexpected ", !IO),
(
Const = term.float(Float),
io.write_string(" float `", !IO),
io.write_float(Float, !IO),
io.write_string("'", !IO)
;
Const = term.string(String),
io.write_string(" string """, !IO),
io.write_string(String, !IO),
io.write_string("""", !IO)
;
( Const = term.integer(_, _, _, _)
; Const = term.atom(_)
; Const = term.implementation_defined(_)
),
error("report_eval_error")
),
io.nl(!IO).
:- func eval_expr(calc_info, varset, term) = int.
eval_expr(CalcInfo, VarSet, term.variable(Var, _Context)) = Res :-
varset.lookup_name(VarSet, Var, VarName),
( if map.search(CalcInfo, VarName, Res0) then
Res = Res0
else
throw(unknown_variable(VarName))
).
eval_expr(CalcInfo, VarSet, term.functor(term.atom(Op), Args, _)) = Res :-
( 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)))
).
eval_expr(_, _, Term) = Int :-
Term = term.functor(Const, _, Context),
Const = term.integer(_, _, _, _),
( if term_to_int(Term, Int0) then
Int = Int0
else
throw(unexpected_const(Const) - Context)
).
eval_expr(_, _, term.functor(term.float(Float), _, Context)) =
throw(unexpected_const(term.float(Float)) - Context).
eval_expr(_, _, term.functor(term.string(String), _, Context)) =
throw(unexpected_const(term.string(String)) - Context).
eval_expr(_, _, 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 calculator2.ops_table(string::in, op_info::out, list(op_info)::out)
is semidet.
calculator2.ops_table("//", op_info(infix(y, x), 400), []).
calculator2.ops_table("*", op_info(infix(y, x), 400), []).
calculator2.ops_table("+", op_info(infix(y, x), 500),
[op_info(prefix(x), 500)]).
calculator2.ops_table("-", op_info(infix(y, x), 500),
[op_info(prefix(x), 200)]).
calculator2.ops_table("=", op_info(infix(x, x), 700), []).
:- instance ops.op_table(calculator_op_table) where [
( ops.lookup_infix_op(_, Op, Priority, LeftAssoc, RightAssoc) :-
calculator2.ops_table(Op, Info, _),
Info = op_info(infix(LeftAssoc, RightAssoc), Priority)
),
ops.lookup_operator_term(_, _, _, _) :- fail,
( ops.lookup_prefix_op(_, Op, Priority, LeftAssoc) :-
calculator2.ops_table(Op, _, OtherInfo),
OtherInfo = [op_info(prefix(LeftAssoc), Priority)]
),
ops.lookup_postfix_op(_, _, _, _) :- fail,
ops.lookup_binary_prefix_op(_, _, _, _, _) :- fail,
ops.lookup_op(Table, Op) :- ops.lookup_infix_op(Table, Op, _, _, _),
ops.lookup_op(Table, Op) :- ops.lookup_prefix_op(Table, Op, _, _),
ops.lookup_op(Table, Op) :-
ops.lookup_binary_prefix_op(Table, Op, _, _, _),
ops.lookup_op(Table, Op) :- ops.lookup_postfix_op(Table, Op, _, _),
ops.lookup_op_infos(_, Op, OpInfo, OtherInfos) :-
calculator2.ops_table(Op, OpInfo, OtherInfos),
ops.max_priority(_) = 700,
ops.arg_priority(Table) = ops.max_priority(Table) + 1
].
%-----------------------------------------------------------------------------%
:- end_module calculator2.
%-----------------------------------------------------------------------------%