interpl

Experiments with intepreters in Prolog
git clone https://ccx.te2000.cz/git/interpl
Log | Files | Refs | README

commit d8d881a4f32ea30e4893ffecc45820b93ef3553f
parent 85cddd3cf0a0b5371c41078f4c1ee89cb2052219
Author: Jan Pobrislo <ccx@te2000.cz>
Date:   Fri, 30 May 2025 18:35:18 +0000

Error handling, eval tests, fix quotation.

Diffstat:
Minterpreter.pl | 52+++++++++++++++++++++++++++++++++++++---------------
Mtests.pl | 33+++++++++++++++++++++++++++++++--
2 files changed, 68 insertions(+), 17 deletions(-)

diff --git a/interpreter.pl b/interpreter.pl @@ -5,6 +5,8 @@ , sexp/4 , interp_apply/3 , interp_eval/4 + , the_global_environment/1 + , primitive_arity/2 ]). :- use_module(library(debug), [assertion/1]). :- use_module(library(dcg/basics), [blanks//0, number//1]). @@ -51,7 +53,10 @@ extend_environment(OldEnv, VarNames, Values, [NewFrame|OldEnv]) :- pairs_keys_values(NewFrame, VarNames, Values). lookup_variable_value(Env, VarName, Value) :- - lookup_variable_value_aux(Env, VarName, Value) -> true ; assertion(false). % unbound variable + Here=lookup_variable_value/3, + ( lookup_variable_value_aux(Env, VarName, Value) -> true + ; throw(error(interp_error(unbound_var(VarName)), context(Here, 'Unbound variable.')))). + lookup_variable_value_aux([Frame|Env], VarName, Value) :- lookup_variable_from_frame(Frame, VarName, Value) -> true ; lookup_variable_value_aux(Env, VarName, Value). lookup_variable_from_frame([K-V|Rest], VarName, Value) :- @@ -79,7 +84,9 @@ eval_definition([define, VarName, Definition], Value, OldEnv, NewEnv) :- is_assignment(['set!'|_]). set_variable_value(VarName, Value, OldEnv, NewEnv) :- - set_variable_value_aux(VarName, Value, OldEnv, NewEnv) -> true ; assertion(false). + Here=set_variable_value/4, + ( set_variable_value_aux(VarName, Value, OldEnv, NewEnv) -> true + ; throw(error(interp_error(unbound_var(VarName)), context(Here, 'Unbound variable.')))). set_variable_value_aux(VarName, Value, [OldFrame|OldE], [NewFrame|NewE]) :- ( set_variable_in_frame(VarName, Value, OldFrame, NewFrame) @@ -142,7 +149,7 @@ rest_operands([_,_|X], X). interp_eval(Expression, Expression, Env, Env) :- self_evaluating(Expression), !. -interp_eval([quoted|Rest], Rest, Env, Env) :- !. +interp_eval([quote, Text], Text, Env, Env) :- !. interp_eval(VarName, Result, Env, Env) :- variable(VarName), !, @@ -155,15 +162,22 @@ interp_eval(Expression, void, Env, NewEnv) :- !, eval_assignment(Expression, Env, NewEnv). interp_eval([lambda, Parameters|Body], Result, Env, Env) :- + !, make_procedure(Parameters, Body, Env, Result). interp_eval([Operator|Operands], Result, Env, NewEnv) :- !, interp_eval(Operator, Procedure, Env, NewEnv), list_of_values(Operands, Env, Arguments), interp_apply(Procedure, Arguments, Result). -interp_eval(_, _, _, _) :- assertion(false). - -interp_apply(primitive(Name), Arguments, Result) :- +interp_eval(Expression, _, _, _) :- + Here=interp_eval/4, + throw(error(interp_error(eval, Expression), context(Here, 'Don\'t know how to evaluate this expression.'))). + +interp_apply(primitive(Name, Arity), Arguments, Result) :- + Here = interp_apply/3, + length(Arguments, ALength), + ( Arity = ALength -> true + ; throw(error(interp_error(arity, Name), context(Here, 'Arity error.')))), primitive_apply(Name, Arguments, Result). interp_apply(proc(Parameters, Body, Env), Arguments, Result) :- extend_environment(Env, Parameters, Arguments, NewEnv), @@ -181,7 +195,7 @@ eval_sequence_aux([H|T], Env, _, Result) :- list_of_values([], _, []). list_of_values([Name|NameTail], Env, [Value|ValueTail]) :- interp_eval(Name, Value, Env, Env), % enforces environment does not change - list_of_values(NameTail, ValueTail). + list_of_values(NameTail, Env, ValueTail). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Environment Setup @@ -192,14 +206,13 @@ primitive_apply(cons, [H,T], [H|T]). primitive_apply(+, [A, B], Result) :- Result is A + B. % primitive_apply('[]', [], []). -setup_environment([[ - car-primitive(car), - cdr-primitive(cdr), - cons-primitive(cons), - (+)-primitive(+), - true-true, - false-false -]]). +primitive_arity(car, 1). +primitive_arity(cdr, 1). +primitive_arity(cons, 2). +primitive_arity(+, 2). + +setup_environment([[true-true, false-false|PrimitiveProcs]]) :- + findall(Name-primitive(Name, Arity), primitive_arity(Name, Arity), PrimitiveProcs). the_global_environment(Env) :- setup_environment(Env). @@ -236,6 +249,15 @@ prolog:error_message(parse_error(Codes)) --> prolog:error_message(evaluation_error(Codes)) --> [ 'Failed to evaluate expression: "~s".' - [Codes] ]. +prolog:error_message(interp_error(eval, Expression)) --> + [ 'Don\'t know how to evaluate: "~w".' - [Expression] ]. + +prolog:error_message(interp_error(arity, Func)) --> + [ 'Invalid amount of arguments for function: "~w".' - [Func] ]. + +prolog:error_message(interp_error(unbound_var, VarName)) --> + [ 'No such variable: "~w".' - [VarName] ]. + % grammar {{{1 parse_input_expression(Codes, Expression) :- diff --git a/tests.pl b/tests.pl @@ -50,7 +50,6 @@ test(sexp_qq) :- assertion(Expression =@= [define, proc, [lambda, [e, f, g], [lambda, [], e]]]). :- end_tests(sexp). -:- begin_tests(primitive_apply). apply_example(car, [{|sexp||(a b c)|}], {|sexp||a|}). apply_example(car, [{|sexp||((a b c) x y z)|}], {|sexp||(a b c)|}). @@ -78,8 +77,38 @@ apply_example(`(cons (a b (c)) ())`, `((a b (c)))`). apply_example(`(+ 0 0)`, `0`). apply_example(`(+ 3 2)`, `5`). +:- begin_tests(primitive_apply). test(apply_example, [forall(apply_example(Func, Args, ExpectedResult))]) :- - interp_apply(primitive(Func), Args, Result), + primitive_arity(Func, Arity), + interp_apply(primitive(Func, Arity), Args, Result), assertion(Result =@= ExpectedResult). +test(apply_arity_error, [error(interp_error(arity, car))]) :- + Func=car, + primitive_arity(Func, Arity), + interp_apply(primitive(Func, Arity), [[a], [b]], _). + :- end_tests(primitive_apply). +:- begin_tests(eval). + +quoted_args([], []). +quoted_args([H|T], [[quote, H]|QT]) :- quoted_args(T, QT). + +test_eval(Call, ExpectedResult) :- + format('testing call: ~w → expecting ~w~n', [Call, ExpectedResult]), + the_global_environment(Env), + interp_eval(Call, Result, Env, _), + assertion(Result =@= ExpectedResult). + +test(eval_quote) :- + test_eval({|sexp||(quote a)|}, a). + +test(eval_quote2) :- + test_eval({|sexp||(quote ())|}, []). + +test(eval_apply_example, [forall(apply_example(Func, Args, ExpectedResult))]) :- + quoted_args(Args, QArgs), + Call = [Func|QArgs], + test_eval(Call, ExpectedResult). + +:- end_tests(eval).