1 with Ada
.Environment_Variables
;
2 with Ada
.Text_IO
.Unbounded_IO
;
7 with Garbage_Collected
;
16 procedure Step5_Tco
is
18 Dbgeval
: constant Boolean := Ada
.Environment_Variables
.Exists
("dbgeval");
21 use all type Types
.Kind_Type
;
22 use type Types
.Strings
.Instance
;
24 function Read
return Types
.T_Array
with Inline
;
26 function Eval
(Ast0
: in Types
.T
;
27 Env0
: in Envs
.Ptr
) return Types
.T
;
29 procedure Print
(Ast
: in Types
.T
) with Inline
;
31 procedure Rep
(Env
: in Envs
.Ptr
) with Inline
;
33 function Eval_Map
(Source
: in Types
.Maps
.Instance
;
34 Env
: in Envs
.Ptr
) return Types
.T
;
35 function Eval_Vector
(Source
: in Types
.Sequences
.Instance
;
36 Env
: in Envs
.Ptr
) return Types
.T
;
37 -- Helpers for the Eval function.
39 procedure Exec
(Script
: in String;
40 Env
: in Envs
.Ptr
) with Inline
;
41 -- Read the script, eval its elements, but ignore the result.
43 ----------------------------------------------------------------------
45 function Eval
(Ast0
: in Types
.T
;
46 Env0
: in Envs
.Ptr
) return Types
.T
48 -- Use local variables, that can be rewritten when tail call
49 -- optimization goes to <<Restart>>.
50 Ast
: Types
.T
:= Ast0
;
51 Env
: Envs
.Ptr
:= Env0
;
52 Env_Reusable
: Boolean := False;
53 -- True when the environment has been created in this recursion
54 -- level, and has not yet been referenced by a closure. If so,
55 -- we can reuse it instead of creating a subenvironment.
61 Ada
.Text_IO
.Put
("EVAL: ");
63 Envs
.Dump_Stack
(Env
.all);
67 when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types
.Kind_Key
68 | Kind_Macro | Types
.Kind_Function
=>
71 return Env
.all.Get
(Ast
.Str
);
73 return Eval_Map
(Ast
.Map
.all, Env
);
75 return Eval_Vector
(Ast
.Sequence
.all, Env
);
81 if Ast
.Sequence
.all.Length
= 0 then
84 First
:= Ast
.Sequence
.all.Data
(1);
87 -- Ast is a non-empty list, First is its first element.
90 if First
.Str
.all = "if" then
91 Err
.Check
(Ast
.Sequence
.all.Length
in 3 .. 4,
92 "expected 2 or 3 parameters");
94 Tst
: constant Types
.T
:= Eval
(Ast
.Sequence
.all.Data
(2), Env
);
96 if Tst
/= Types
.Nil
and Tst
/= (Kind_Boolean
, False) then
97 Ast
:= Ast
.Sequence
.all.Data
(3);
99 elsif Ast
.Sequence
.all.Length
= 3 then
102 Ast
:= Ast
.Sequence
.all.Data
(4);
106 elsif First
.Str
.all = "let*" then
107 Err
.Check
(Ast
.Sequence
.all.Length
= 3
108 and then Ast
.Sequence
.all.Data
(2).Kind
in Types
.Kind_Sequence
,
109 "expected a sequence then a value");
111 Bindings
: Types
.T_Array
112 renames Ast
.Sequence
.all.Data
(2).Sequence
.all.Data
;
114 Err
.Check
(Bindings
'Length mod 2 = 0, "expected even binds");
115 if not Env_Reusable
then
116 Env
:= Envs
.New_Env
(Outer
=> Env
);
117 Env_Reusable
:= True;
119 for I
in 0 .. Bindings
'Length / 2 - 1 loop
120 Env
.all.Set
(Bindings
(Bindings
'First + 2 * I
),
121 Eval
(Bindings
(Bindings
'First + 2 * I
+ 1), Env
));
122 -- This call checks key kind.
124 Ast
:= Ast
.Sequence
.all.Data
(3);
127 elsif First
.Str
.all = "def!" then
128 Err
.Check
(Ast
.Sequence
.all.Length
= 3, "expected 2 parameters");
130 Key
: Types
.T
renames Ast
.Sequence
.all.Data
(2);
131 Val
: constant Types
.T
:= Eval
(Ast
.Sequence
.all.Data
(3), Env
);
133 Env
.all.Set
(Key
, Val
); -- Check key kind.
136 -- do is a built-in function, shortening this test cascade.
137 elsif First
.Str
.all = "fn*" then
138 Err
.Check
(Ast
.Sequence
.all.Length
= 3, "expected 2 parameters");
140 Params
: Types
.T
renames Ast
.Sequence
.all.Data
(2);
142 Err
.Check
(Params
.Kind
in Types
.Kind_Sequence
,
143 "first argument of fn* must be a sequence");
144 Env_Reusable
:= False;
145 return Types
.Fns
.New_Function
146 (Params
=> Params
.Sequence
,
147 Ast
=> Ast
.Sequence
.all.Data
(3),
151 -- Equivalent to First := Eval (First, Env)
152 -- except that we already know enough to spare a recursive call.
153 First
:= Env
.all.Get
(First
.Str
);
155 when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types
.Kind_Key
156 | Kind_Macro | Types
.Kind_Function
=>
157 -- Equivalent to First := Eval (First, Env)
158 -- except that we already know enough to spare a recursive call.
160 when Types
.Kind_Sequence | Kind_Map
=>
161 -- Lists are definitely worth a recursion, and the two other
162 -- cases should be rare (they will report an error later).
163 First
:= Eval
(First
, Env
);
167 -- Ast is a non-empty list,
168 -- First is its non-special evaluated first element.
169 Err
.Check
(First
.Kind
in Types
.Kind_Function
,
170 "first element must be a function");
171 -- We are applying a function. Evaluate its arguments.
173 Args
: Types
.T_Array
(2 .. Ast
.Sequence
.all.Length
);
175 for I
in Args
'Range loop
176 Args
(I
) := Eval
(Ast
.Sequence
.all.Data
(I
), Env
);
178 if First
.Kind
= Kind_Builtin
then
179 return First
.Builtin
.all (Args
);
181 -- Like Types.Fns.Apply, except that we use TCO.
182 Env
:= Envs
.New_Env
(Outer
=> First
.Fn
.all.Env
);
183 Env_Reusable
:= True;
184 Env
.all.Set_Binds
(Binds
=> First
.Fn
.all.Params
.all.Data
,
186 Ast
:= First
.Fn
.all.Ast
;
191 Err
.Add_Trace_Line
("eval", Ast
);
195 function Eval_Map
(Source
: in Types
.Maps
.Instance
;
196 Env
: in Envs
.Ptr
) return Types
.T
198 use all type Types
.Maps
.Cursor
;
199 -- Copy the whole map so that keys are not hashed again.
200 Result
: constant Types
.T
:= Types
.Maps
.New_Map
(Source
);
201 Position
: Types
.Maps
.Cursor
:= Result
.Map
.all.First
;
203 while Has_Element
(Position
) loop
204 Result
.Map
.all.Replace_Element
(Position
,
205 Eval
(Element
(Position
), Env
));
211 function Eval_Vector
(Source
: in Types
.Sequences
.Instance
;
212 Env
: in Envs
.Ptr
) return Types
.T
214 Ref
: constant Types
.Sequence_Ptr
215 := Types
.Sequences
.Constructor
(Source
.Length
);
217 for I
in Source
.Data
'Range loop
218 Ref
.all.Data
(I
) := Eval
(Source
.Data
(I
), Env
);
220 return (Kind_Vector
, Ref
);
223 procedure Exec
(Script
: in String;
228 for Expression
of Reader
.Read_Str
(Script
) loop
229 Result
:= Eval
(Expression
, Env
);
231 pragma Unreferenced
(Result
);
234 procedure Print
(Ast
: in Types
.T
) is
236 Ada
.Text_IO
.Unbounded_IO
.Put_Line
(Printer
.Pr_Str
(Ast
));
239 function Read
return Types
.T_Array
240 is (Reader
.Read_Str
(Readline
.Input
("user> ")));
242 procedure Rep
(Env
: in Envs
.Ptr
) is
244 for Expression
of Read
loop
245 Print
(Eval
(Expression
, Env
));
249 ----------------------------------------------------------------------
251 Startup
: constant String
252 := "(def! not (fn* (a) (if a false true)))";
253 Repl
: constant Envs
.Ptr
:= Envs
.New_Env
;
255 -- Show the Eval function to other packages.
256 Types
.Fns
.Eval_Cb
:= Eval
'Unrestricted_Access;
257 -- Add Core functions into the top environment.
258 Core
.NS_Add_To_Repl
(Repl
);
259 -- Native startup procedure.
260 Exec
(Startup
, Repl
);
261 -- Execute user commands.
266 when Readline
.End_Of_File
=>
269 Ada
.Text_IO
.Unbounded_IO
.Put
(Err
.Trace
);
271 -- Other exceptions are really unexpected.
274 Err
.Data
:= Types
.Nil
;
276 Garbage_Collected
.Clean
;
278 Ada
.Text_IO
.New_Line
;
280 -- If assertions are enabled, check deallocations.
281 -- Normal runs do not need to deallocate before termination.
282 -- Beware that all pointers are now dangling.
283 pragma Debug
(Garbage_Collected
.Clean
);
284 Garbage_Collected
.Check_Allocations
;