2 with Ada
.Environment_Variables
;
3 with Ada
.Text_IO
.Unbounded_IO
;
8 with Garbage_Collected
;
17 procedure Step6_File
is
19 Dbgeval
: constant Boolean := Ada
.Environment_Variables
.Exists
("dbgeval");
22 use all type Types
.Kind_Type
;
23 use type Types
.Strings
.Instance
;
24 package ACL
renames Ada
.Command_Line
;
26 function Read
return Types
.T_Array
with Inline
;
28 function Eval
(Ast0
: in Types
.T
;
29 Env0
: in Envs
.Ptr
) return Types
.T
;
30 function Eval_Builtin
(Args
: in Types
.T_Array
) return Types
.T
;
31 -- The built-in variant needs to see the Repl variable.
33 procedure Print
(Ast
: in Types
.T
) with Inline
;
35 procedure Rep
(Env
: in Envs
.Ptr
) with Inline
;
37 function Eval_Map
(Source
: in Types
.Maps
.Instance
;
38 Env
: in Envs
.Ptr
) return Types
.T
;
39 function Eval_Vector
(Source
: in Types
.Sequences
.Instance
;
40 Env
: in Envs
.Ptr
) return Types
.T
;
41 -- Helpers for the Eval function.
43 procedure Exec
(Script
: in String;
44 Env
: in Envs
.Ptr
) with Inline
;
45 -- Read the script, eval its elements, but ignore the result.
47 ----------------------------------------------------------------------
49 function Eval
(Ast0
: in Types
.T
;
50 Env0
: in Envs
.Ptr
) return Types
.T
52 -- Use local variables, that can be rewritten when tail call
53 -- optimization goes to <<Restart>>.
54 Ast
: Types
.T
:= Ast0
;
55 Env
: Envs
.Ptr
:= Env0
;
56 Env_Reusable
: Boolean := False;
57 -- True when the environment has been created in this recursion
58 -- level, and has not yet been referenced by a closure. If so,
59 -- we can reuse it instead of creating a subenvironment.
65 Ada
.Text_IO
.Put
("EVAL: ");
67 Envs
.Dump_Stack
(Env
.all);
71 when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types
.Kind_Key
72 | Kind_Macro | Types
.Kind_Function
=>
75 return Env
.all.Get
(Ast
.Str
);
77 return Eval_Map
(Ast
.Map
.all, Env
);
79 return Eval_Vector
(Ast
.Sequence
.all, Env
);
85 if Ast
.Sequence
.all.Length
= 0 then
88 First
:= Ast
.Sequence
.all.Data
(1);
91 -- Ast is a non-empty list, First is its first element.
94 if First
.Str
.all = "if" then
95 Err
.Check
(Ast
.Sequence
.all.Length
in 3 .. 4,
96 "expected 2 or 3 parameters");
98 Tst
: constant Types
.T
:= Eval
(Ast
.Sequence
.all.Data
(2), Env
);
100 if Tst
/= Types
.Nil
and Tst
/= (Kind_Boolean
, False) then
101 Ast
:= Ast
.Sequence
.all.Data
(3);
103 elsif Ast
.Sequence
.all.Length
= 3 then
106 Ast
:= Ast
.Sequence
.all.Data
(4);
110 elsif First
.Str
.all = "let*" then
111 Err
.Check
(Ast
.Sequence
.all.Length
= 3
112 and then Ast
.Sequence
.all.Data
(2).Kind
in Types
.Kind_Sequence
,
113 "expected a sequence then a value");
115 Bindings
: Types
.T_Array
116 renames Ast
.Sequence
.all.Data
(2).Sequence
.all.Data
;
118 Err
.Check
(Bindings
'Length mod 2 = 0, "expected even binds");
119 if not Env_Reusable
then
120 Env
:= Envs
.New_Env
(Outer
=> Env
);
121 Env_Reusable
:= True;
123 for I
in 0 .. Bindings
'Length / 2 - 1 loop
124 Env
.all.Set
(Bindings
(Bindings
'First + 2 * I
),
125 Eval
(Bindings
(Bindings
'First + 2 * I
+ 1), Env
));
126 -- This call checks key kind.
128 Ast
:= Ast
.Sequence
.all.Data
(3);
131 elsif First
.Str
.all = "def!" then
132 Err
.Check
(Ast
.Sequence
.all.Length
= 3, "expected 2 parameters");
134 Key
: Types
.T
renames Ast
.Sequence
.all.Data
(2);
135 Val
: constant Types
.T
:= Eval
(Ast
.Sequence
.all.Data
(3), Env
);
137 Env
.all.Set
(Key
, Val
); -- Check key kind.
140 -- do is a built-in function, shortening this test cascade.
141 elsif First
.Str
.all = "fn*" then
142 Err
.Check
(Ast
.Sequence
.all.Length
= 3, "expected 2 parameters");
144 Params
: Types
.T
renames Ast
.Sequence
.all.Data
(2);
146 Err
.Check
(Params
.Kind
in Types
.Kind_Sequence
,
147 "first argument of fn* must be a sequence");
148 Env_Reusable
:= False;
149 return Types
.Fns
.New_Function
150 (Params
=> Params
.Sequence
,
151 Ast
=> Ast
.Sequence
.all.Data
(3),
155 -- Equivalent to First := Eval (First, Env)
156 -- except that we already know enough to spare a recursive call.
157 First
:= Env
.all.Get
(First
.Str
);
159 when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Types
.Kind_Key
160 | Kind_Macro | Types
.Kind_Function
=>
161 -- Equivalent to First := Eval (First, Env)
162 -- except that we already know enough to spare a recursive call.
164 when Types
.Kind_Sequence | Kind_Map
=>
165 -- Lists are definitely worth a recursion, and the two other
166 -- cases should be rare (they will report an error later).
167 First
:= Eval
(First
, Env
);
171 -- Ast is a non-empty list,
172 -- First is its non-special evaluated first element.
173 Err
.Check
(First
.Kind
in Types
.Kind_Function
,
174 "first element must be a function");
175 -- We are applying a function. Evaluate its arguments.
177 Args
: Types
.T_Array
(2 .. Ast
.Sequence
.all.Length
);
179 for I
in Args
'Range loop
180 Args
(I
) := Eval
(Ast
.Sequence
.all.Data
(I
), Env
);
182 if First
.Kind
= Kind_Builtin
then
183 return First
.Builtin
.all (Args
);
185 -- Like Types.Fns.Apply, except that we use TCO.
186 Env
:= Envs
.New_Env
(Outer
=> First
.Fn
.all.Env
);
187 Env_Reusable
:= True;
188 Env
.all.Set_Binds
(Binds
=> First
.Fn
.all.Params
.all.Data
,
190 Ast
:= First
.Fn
.all.Ast
;
195 Err
.Add_Trace_Line
("eval", Ast
);
199 function Eval_Map
(Source
: in Types
.Maps
.Instance
;
200 Env
: in Envs
.Ptr
) return Types
.T
202 use all type Types
.Maps
.Cursor
;
203 -- Copy the whole map so that keys are not hashed again.
204 Result
: constant Types
.T
:= Types
.Maps
.New_Map
(Source
);
205 Position
: Types
.Maps
.Cursor
:= Result
.Map
.all.First
;
207 while Has_Element
(Position
) loop
208 Result
.Map
.all.Replace_Element
(Position
,
209 Eval
(Element
(Position
), Env
));
215 function Eval_Vector
(Source
: in Types
.Sequences
.Instance
;
216 Env
: in Envs
.Ptr
) return Types
.T
218 Ref
: constant Types
.Sequence_Ptr
219 := Types
.Sequences
.Constructor
(Source
.Length
);
221 for I
in Source
.Data
'Range loop
222 Ref
.all.Data
(I
) := Eval
(Source
.Data
(I
), Env
);
224 return (Kind_Vector
, Ref
);
227 procedure Exec
(Script
: in String;
232 for Expression
of Reader
.Read_Str
(Script
) loop
233 Result
:= Eval
(Expression
, Env
);
235 pragma Unreferenced
(Result
);
238 procedure Print
(Ast
: in Types
.T
) is
240 Ada
.Text_IO
.Unbounded_IO
.Put_Line
(Printer
.Pr_Str
(Ast
));
243 function Read
return Types
.T_Array
244 is (Reader
.Read_Str
(Readline
.Input
("user> ")));
246 procedure Rep
(Env
: in Envs
.Ptr
) is
248 for Expression
of Read
loop
249 Print
(Eval
(Expression
, Env
));
253 ----------------------------------------------------------------------
255 Startup
: constant String
256 := "(def! not (fn* (a) (if a false true)))"
257 & "(def! load-file (fn* (f)"
258 & " (eval (read-string (str ""(do "" (slurp f) "")"")))))";
259 Repl
: constant Envs
.Ptr
:= Envs
.New_Env
;
260 function Eval_Builtin
(Args
: in Types
.T_Array
) return Types
.T
is
262 Err
.Check
(Args
'Length = 1, "expected 1 parameter");
263 return Eval
(Args
(Args
'First), Repl
);
265 Script
: constant Boolean := 0 < ACL
.Argument_Count
;
266 Argv
: constant Types
.Sequence_Ptr
267 := Types
.Sequences
.Constructor
(Integer'Max (0, ACL
.Argument_Count
- 1));
269 -- Show the Eval function to other packages.
270 Types
.Fns
.Eval_Cb
:= Eval
'Unrestricted_Access;
271 -- Add Core functions into the top environment.
272 Core
.NS_Add_To_Repl
(Repl
);
273 Repl
.all.Set
((Kind_Symbol
, Types
.Strings
.Alloc
("eval")),
274 (Kind_Builtin
, Eval_Builtin
'Unrestricted_Access));
275 -- Native startup procedure.
276 Exec
(Startup
, Repl
);
277 -- Define ARGV from command line arguments.
278 for I
in 2 .. ACL
.Argument_Count
loop
279 Argv
.all.Data
(I
- 1) := (Kind_String
,
280 Types
.Strings
.Alloc
(ACL
.Argument
(I
)));
282 Repl
.all.Set
((Kind_Symbol
, Types
.Strings
.Alloc
("*ARGV*")),
284 -- Execute user commands.
286 Exec
("(load-file """ & ACL
.Argument
(1) & """)", Repl
);
292 when Readline
.End_Of_File
=>
295 Ada
.Text_IO
.Unbounded_IO
.Put
(Err
.Trace
);
297 -- Other exceptions are really unexpected.
300 Err
.Data
:= Types
.Nil
;
302 Garbage_Collected
.Clean
;
304 Ada
.Text_IO
.New_Line
;
307 -- If assertions are enabled, check deallocations.
308 -- Normal runs do not need to deallocate before termination.
309 -- Beware that all pointers are now dangling.
310 pragma Debug
(Garbage_Collected
.Clean
);
311 Garbage_Collected
.Check_Allocations
;