mirror of
https://github.com/kanaka/mal.git
synced 2024-09-21 02:27:10 +03:00
00c3a3c33d
Two changes require approval. * The 'do' special becomes a built-in function similar to first. This small change reduces the complexity of eval. The last evaluation cannot benefit from TCO, but the performance change seems invisible. * read/eval/print acts on each item found in the input string, as if they were enclosed with (do ..). The guide does not specify what should happen to text following the first AST, and this change actually simplifies some things (like dealing with zero AST). The read-string built-in function only returns the first AST, as changing this would be much more intrusive. Other changes seem straightforward. Global: * Ada 2020 target assignments (like +=, but more general). * Use Constant_Indexing aspect for sequences, so that they can be indexed in source code like native arrays. * consistency renamings. 'fn' does not include built-in functions, 'function' does. 'list' does not include vectors, 'sequence' does. Move error handling to a separate package. * This simplifies code everywhere else. * Uncaught expressions now report a stack trace. Types: * Count allocations and deallocations, check that counts match. * Share more code between functions and macros. Core: * Replace the Core.Ns function returning an array with a procedure (The intermediate object was preventing the reference counting code from deallocating some unused objects). * Implement Prn with Pr_Str. Printer: * Change the profile so that the caller spares some allocations. Reader: * Share a single buffer of mal values between all recursions. This significantly reduces the stack footprint. Steps: * Fix implementation name (ada.2) in the startup script. * Let environment variables trigger debugging information.
161 lines
4.8 KiB
Ada
161 lines
4.8 KiB
Ada
with Ada.Environment_Variables;
|
|
with Ada.Containers.Indefinite_Hashed_Maps;
|
|
with Ada.Strings.Hash;
|
|
with Ada.Text_IO.Unbounded_IO;
|
|
|
|
with Err;
|
|
with Printer;
|
|
with Reader;
|
|
with Readline;
|
|
with Types.Atoms;
|
|
with Types.Builtins;
|
|
with Types.Fns;
|
|
with Types.Mal;
|
|
with Types.Maps;
|
|
with Types.Sequences;
|
|
with Types.Symbols;
|
|
|
|
procedure Step2_Eval is
|
|
|
|
Dbgeval : constant Boolean := Ada.Environment_Variables.Exists ("dbgeval");
|
|
|
|
use Types;
|
|
|
|
package Envs is new Ada.Containers.Indefinite_Hashed_Maps
|
|
(Key_Type => String,
|
|
Element_Type => Mal.Builtin_Ptr,
|
|
Hash => Ada.Strings.Hash,
|
|
Equivalent_Keys => "=",
|
|
"=" => Mal."=");
|
|
|
|
function Read return Mal.T_Array with Inline;
|
|
|
|
function Eval (Ast : in Mal.T;
|
|
Env : in Envs.Map) return Mal.T;
|
|
|
|
procedure Print (Ast : in Mal.T) with Inline;
|
|
|
|
procedure Rep (Env : in Envs.Map) with Inline;
|
|
|
|
generic
|
|
with function Ada_Operator (Left, Right : in Integer) return Integer;
|
|
function Generic_Mal_Operator (Args : in Mal.T_Array) return Mal.T;
|
|
|
|
function Eval_Seq_Elts is new Sequences.Generic_Eval (Envs.Map, Eval);
|
|
function Eval_Map_Elts is new Maps.Generic_Eval (Envs.Map, Eval);
|
|
|
|
----------------------------------------------------------------------
|
|
|
|
function Eval (Ast : in Mal.T;
|
|
Env : in Envs.Map) return Mal.T
|
|
is
|
|
First : Mal.T;
|
|
begin
|
|
if Dbgeval then
|
|
Ada.Text_IO.New_Line;
|
|
Ada.Text_IO.Put ("EVAL: ");
|
|
Print (Ast);
|
|
end if;
|
|
case Ast.Kind is
|
|
when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Kind_Key
|
|
| Kind_Macro | Kind_Function =>
|
|
return Ast;
|
|
when Kind_Symbol =>
|
|
declare
|
|
S : constant String := Ast.Symbol.To_String;
|
|
C : constant Envs.Cursor := Env.Find (S);
|
|
begin
|
|
-- The predefined error message does not pass tests.
|
|
Err.Check (Envs.Has_Element (C), "'" & S & "' not found");
|
|
return (Kind_Builtin, Envs.Element (C));
|
|
end;
|
|
when Kind_Map =>
|
|
return Eval_Map_Elts (Ast.Map, Env);
|
|
when Kind_Vector =>
|
|
return (Kind_Vector, Eval_Seq_Elts (Ast.Sequence, Env));
|
|
when Kind_List =>
|
|
null;
|
|
end case;
|
|
|
|
-- Ast is a list.
|
|
if Ast.Sequence.Length = 0 then
|
|
return Ast;
|
|
end if;
|
|
First := Eval (Ast.Sequence (1), Env);
|
|
|
|
-- Apply phase.
|
|
-- Ast is a non-empty list,
|
|
-- First is its non-special evaluated first element.
|
|
case First.Kind is
|
|
when Kind_Builtin =>
|
|
declare
|
|
Args : Mal.T_Array (2 .. Ast.Sequence.Length);
|
|
begin
|
|
for I in Args'Range loop
|
|
Args (I) := Eval (Ast.Sequence (I), Env);
|
|
end loop;
|
|
return First.Builtin.all (Args);
|
|
end;
|
|
when others =>
|
|
Err.Raise_With ("first element must be a function");
|
|
end case;
|
|
exception
|
|
when Err.Error =>
|
|
Err.Add_Trace_Line ("eval", Ast);
|
|
raise;
|
|
end Eval;
|
|
|
|
function Generic_Mal_Operator (Args : in Mal.T_Array) return Mal.T
|
|
is (Kind_Number, Ada_Operator (Args (Args'First).Number,
|
|
Args (Args'Last).Number));
|
|
|
|
procedure Print (Ast : in Mal.T) is
|
|
begin
|
|
Ada.Text_IO.Unbounded_IO.Put_Line (Printer.Pr_Str (Ast));
|
|
end Print;
|
|
|
|
function Read return Mal.T_Array
|
|
is (Reader.Read_Str (Readline.Input ("user> ")));
|
|
|
|
procedure Rep (Env : in Envs.Map) is
|
|
begin
|
|
for Expression of Read loop
|
|
Print (Eval (Expression, Env));
|
|
end loop;
|
|
end Rep;
|
|
|
|
----------------------------------------------------------------------
|
|
|
|
function Addition is new Generic_Mal_Operator ("+");
|
|
function Subtraction is new Generic_Mal_Operator ("-");
|
|
function Product is new Generic_Mal_Operator ("*");
|
|
function Division is new Generic_Mal_Operator ("/");
|
|
|
|
Repl : Envs.Map;
|
|
begin
|
|
Repl.Insert ("+", Addition 'Unrestricted_Access);
|
|
Repl.Insert ("-", Subtraction'Unrestricted_Access);
|
|
Repl.Insert ("*", Product 'Unrestricted_Access);
|
|
Repl.Insert ("/", Division 'Unrestricted_Access);
|
|
loop
|
|
begin
|
|
Rep (Repl);
|
|
exception
|
|
when Readline.End_Of_File =>
|
|
exit;
|
|
when Err.Error =>
|
|
Ada.Text_IO.Unbounded_IO.Put (Err.Trace);
|
|
end;
|
|
-- Other exceptions are really unexpected.
|
|
end loop;
|
|
Ada.Text_IO.New_Line;
|
|
-- If assertions are enabled, check deallocations.
|
|
Err.Data := Mal.Nil; -- Remove references to other packages
|
|
pragma Debug (Atoms.Check_Allocations);
|
|
pragma Debug (Builtins.Check_Allocations);
|
|
pragma Debug (Fns.Check_Allocations);
|
|
pragma Debug (Maps.Check_Allocations);
|
|
pragma Debug (Sequences.Check_Allocations);
|
|
pragma Debug (Symbols.Check_Allocations);
|
|
end Step2_Eval;
|