mirror of
https://github.com/kanaka/mal.git
synced 2024-11-13 11:23:59 +03:00
Major rewrite, improving style and improving performances.
This commit is contained in:
parent
cbbb51b465
commit
daffc668e9
@ -1,23 +1,16 @@
|
||||
# Variables expected on the command line:
|
||||
OPT := -O2
|
||||
GNATN := -gnatn
|
||||
GNATP := -gnatp
|
||||
ADAFLAGS :=
|
||||
LDFLAGS :=
|
||||
DEBUG :=
|
||||
|
||||
ifdef DEBUG
|
||||
# Some warnings require -O1.
|
||||
OPT := -O1
|
||||
GNATN :=
|
||||
GNATP :=
|
||||
ADAFLAGS := -Wall -Wextra -gnatw.eH.Y -gnatySdouxy -gnatVa -g -gnataEfoqQ \
|
||||
-fstack-check -pg
|
||||
LDFLAGS := -pg
|
||||
else
|
||||
# -O3 is not recommended as the default by the GCC documentation,
|
||||
# and -O2 seems to produce slightly better performances.
|
||||
# See README for a discussion of -gnatp.
|
||||
ADAFLAGS := -O2 -gnatnp
|
||||
endif
|
||||
|
||||
# Compiler arguments.
|
||||
CARGS = -gnat2012 $(OPT) $(GNATN) $(GNATP) $(ADAFLAGS)
|
||||
CARGS = -gnat2012 $(OPT) $(ADAFLAGS)
|
||||
# Linker arguments.
|
||||
LARGS = $(LDFLAGS) -lreadline
|
||||
|
||||
@ -42,15 +35,18 @@ clean:
|
||||
# Tell Make how to detect out-of-date executables, and let gnatmake do
|
||||
# the rest when it must be executed.
|
||||
TYPES := \
|
||||
atoms.ads atoms.adb \
|
||||
environments.ads environments.adb \
|
||||
lists.ads lists.adb \
|
||||
maps.ads maps.adb \
|
||||
names.ads \
|
||||
printer.ads printer.adb \
|
||||
reader.ads reader.adb \
|
||||
types.ads types.adb \
|
||||
strings.ads strings.adb
|
||||
environments.ads environments.adb \
|
||||
printer.ads printer.adb \
|
||||
reader.ads reader.adb \
|
||||
types-atoms.ads types-atoms.adb \
|
||||
types-builtins.ads types-builtins.adb \
|
||||
types-functions.ads types-functions.adb \
|
||||
types-lists.ads types-lists.adb \
|
||||
types-mal.ads types-mal.adb \
|
||||
types-maps.ads types-maps.adb \
|
||||
types-symbols-names.ads \
|
||||
types-symbols.ads types-symbols.adb \
|
||||
types.ads
|
||||
CORE := \
|
||||
core.ads core.adb
|
||||
|
||||
@ -61,11 +57,14 @@ $(stepa) : stepA%: stepa%.adb $(TYPES) $(CORE)
|
||||
$(steps) :
|
||||
gnatmake $< -o $@ -cargs $(CARGS) -largs $(LARGS)
|
||||
|
||||
# Step 8 freezes during the "(or)" test with -gnatp.
|
||||
step8%: GNATP :=
|
||||
|
||||
# The compiler crashes on types.adb with -gnatn.
|
||||
$(step13) $(step49) $(stepa): types.o
|
||||
types.o: GNATN :=
|
||||
types.o: $(TYPES)
|
||||
gcc -c $(CARGS) types.adb
|
||||
.PHONY: steps.diff
|
||||
steps.diff:
|
||||
diff -u step1*.adb step2*.adb; \
|
||||
diff -u step2*.adb step3*.adb; \
|
||||
diff -u step3*.adb step4*.adb; \
|
||||
diff -u step4*.adb step5*.adb; \
|
||||
diff -u step5*.adb step6*.adb; \
|
||||
diff -u step6*.adb step7*.adb; \
|
||||
diff -u step7*.adb step8*.adb; \
|
||||
diff -u step8*.adb step9*.adb; \
|
||||
diff -u step9*.adb stepa*.adb || true
|
||||
|
42
ada2/README
Normal file
42
ada2/README
Normal file
@ -0,0 +1,42 @@
|
||||
Comparison with the first Ada implementation.
|
||||
|
||||
The first implementation was deliberately compatible with all Ada
|
||||
compilers, while this one illustrates various Ada 2012 features, like
|
||||
assertions, preconditions, invariants, initial assignment for limited
|
||||
types, limited imports...
|
||||
|
||||
The variant MAL type is implemented with a discriminant instead of
|
||||
object-style dispatching. This allows more static and dynamic checks,
|
||||
but also two crucial performance improvements:
|
||||
* Nil, boolean and integers are passed by value without dynamic
|
||||
allocation.
|
||||
* Lists are implemented as C-style arrays, and most of them can be
|
||||
allocated on the stack.
|
||||
|
||||
Once each component has an explicit interface, various optimizations
|
||||
have been added: unique allocation of symbols, stack-style allocation
|
||||
of environments in the current execution path, reuse of existing
|
||||
memory when the reference count reaches 1...
|
||||
|
||||
The eventual performances compete with C-style languages, allthough
|
||||
all user input is checked (implicit language-defined checks like array
|
||||
bounds and discriminant consistency are only enabled during tests).
|
||||
|
||||
There are also similarities with the first implementation. For
|
||||
example, both rely on user-defined finalization to handle recursive
|
||||
structures without garbage collecting.
|
||||
|
||||
|
||||
About reference reference counting.
|
||||
|
||||
* The finalize procedure may be called twice, so it does nothing when
|
||||
the reference count is zero, meaning that we are reaching Finalize
|
||||
recursively.
|
||||
* In implementations, a consistent object (that will be deallocated
|
||||
automatically) must be built before any exception is raised by user
|
||||
code (for example 'map' may run user functions).
|
||||
|
||||
|
||||
Known bugs: the third step of the perf^ada2 target fails during the
|
||||
final storage deallocation when the executable is built with -gnatp. I
|
||||
have failed to understand why so far.
|
@ -1,52 +0,0 @@
|
||||
with Ada.Unchecked_Deallocation;
|
||||
with Types;
|
||||
|
||||
package body Atoms is
|
||||
|
||||
type Atom_Record is limited record
|
||||
Data : Types.Mal_Type;
|
||||
Refs : Positive;
|
||||
end record;
|
||||
|
||||
procedure Free is new Ada.Unchecked_Deallocation (Object => Atom_Record,
|
||||
Name => Atom_Access);
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
procedure Adjust (Object : in out Ptr) is
|
||||
begin
|
||||
if Object.Ref /= null then
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs + 1;
|
||||
end if;
|
||||
end Adjust;
|
||||
|
||||
function Alloc (New_Value : in Types.Mal_Type) return Ptr
|
||||
is (Ada.Finalization.Controlled with
|
||||
Ref => new Atom_Record'(Data => New_Value,
|
||||
Refs => 1));
|
||||
|
||||
function Deref (Container : in Ptr) return Types.Mal_Type is
|
||||
(Container.Ref.all.Data);
|
||||
|
||||
procedure Finalize (Object : in out Ptr)
|
||||
is
|
||||
Refs : Positive;
|
||||
begin
|
||||
if Object.Ref /= null then
|
||||
Refs := Object.Ref.all.Refs;
|
||||
if 1 < Refs then
|
||||
Object.Ref.all.Refs := Refs - 1;
|
||||
Object.Ref := null;
|
||||
else
|
||||
Free (Object.Ref);
|
||||
end if;
|
||||
end if;
|
||||
end Finalize;
|
||||
|
||||
procedure Set (Container : in Ptr;
|
||||
New_Value : in Types.Mal_Type) is
|
||||
begin
|
||||
Container.Ref.all.Data := New_Value;
|
||||
end Set;
|
||||
|
||||
end Atoms;
|
@ -1,36 +0,0 @@
|
||||
private with Ada.Finalization;
|
||||
limited with Types;
|
||||
|
||||
package Atoms is
|
||||
|
||||
-- Equivalent to a Lists.Ptr with zero or one elements.
|
||||
|
||||
type Ptr is tagged private;
|
||||
No_Element : constant Ptr;
|
||||
|
||||
function Alloc (New_Value : in Types.Mal_Type) return Ptr
|
||||
with Inline;
|
||||
|
||||
function Deref (Container : in Ptr) return Types.Mal_Type
|
||||
with Inline, Pre => Container /= No_Element;
|
||||
|
||||
procedure Set (Container : in Ptr;
|
||||
New_Value : in Types.Mal_Type)
|
||||
with Inline, Pre => Container /= No_Element;
|
||||
|
||||
private
|
||||
|
||||
type Atom_Record;
|
||||
type Atom_Access is access Atom_Record;
|
||||
type Ptr is new Ada.Finalization.Controlled with record
|
||||
Ref : Atom_Access := null;
|
||||
end record;
|
||||
overriding procedure Adjust (Object : in out Ptr)
|
||||
with Inline;
|
||||
overriding procedure Finalize (Object : in out Ptr)
|
||||
with Inline;
|
||||
-- Predefined equality is fine.
|
||||
|
||||
No_Element : constant Ptr := (Ada.Finalization.Controlled with Ref => null);
|
||||
|
||||
end Atoms;
|
927
ada2/core.adb
927
ada2/core.adb
@ -2,580 +2,477 @@ with Ada.Calendar; use type Ada.Calendar.Time;
|
||||
with Ada.Characters.Latin_1;
|
||||
with Ada.Strings.Unbounded;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
with Atoms; use type Atoms.Ptr;
|
||||
with Lists;
|
||||
with Maps;
|
||||
with Names;
|
||||
|
||||
with Environments;
|
||||
with Types.Atoms;
|
||||
with Types.Builtins;
|
||||
with Types.Functions;
|
||||
with Types.Lists;
|
||||
with Types.Maps;
|
||||
with Types.Symbols.Names;
|
||||
with Printer;
|
||||
with Reader;
|
||||
with Strings; use type Strings.Ptr;
|
||||
|
||||
package body Core is
|
||||
|
||||
package ASU renames Ada.Strings.Unbounded;
|
||||
use Types;
|
||||
use Types.Lists;
|
||||
use type Mal.T;
|
||||
use type Mal.T_Array;
|
||||
|
||||
Start_Time : constant Ada.Calendar.Time := Ada.Calendar.Clock;
|
||||
|
||||
Eval : Eval_Callback_Type;
|
||||
function Apply (Func : in Mal.T;
|
||||
Args : in Mal.T_Array;
|
||||
Name : in String) return Mal.T with Inline;
|
||||
-- If Func is not executable, report an exception using "name" as
|
||||
-- the built-in function name.
|
||||
|
||||
function Concatenation_Of_Pr_Str
|
||||
(Args : in Mal_Type_Array;
|
||||
Print_Readably : in Boolean := True;
|
||||
Separator : in String := " ")
|
||||
return Ada.Strings.Unbounded.Unbounded_String;
|
||||
|
||||
function Apply (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Assoc (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Atom (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Concat (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Conj (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Cons (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Contains (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Count (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Deref (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Dissoc (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Equals (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function First (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Get (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Hash_Map (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Is_Empty (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Is_False (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Is_Sequential (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Is_True (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Keys (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Keyword (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function List (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Map (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Meta (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Nth (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Pr_Str (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Println (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Prn (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Read_String (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Readline (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Reset (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Rest (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Seq (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Slurp (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Str (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Swap (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Symbol (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Throw (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Time_Ms (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Vals (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Vector (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function With_Meta (Args : in Mal_Type_Array) return Mal_Type;
|
||||
generic
|
||||
Kind : in Kind_Type;
|
||||
Name : in String;
|
||||
function Generic_Kind_Test (Args : in Mal.T_Array) return Mal.T;
|
||||
function Generic_Kind_Test (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1
|
||||
then raise Argument_Error with Name & ": expects 1 argument"
|
||||
else (Kind_Boolean, Args (Args'First).Kind = Kind));
|
||||
|
||||
generic
|
||||
with function Ada_Operator (Left, Right : in Integer) return Integer;
|
||||
function Generic_Mal_Operator (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Generic_Mal_Operator (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Number, Atoms.No_Element,
|
||||
Ada_Operator (Args (Args'First).Integer_Value,
|
||||
Args (Args'First + 1).Integer_Value));
|
||||
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 ("/");
|
||||
Name : in String;
|
||||
function Generic_Mal_Operator (Args : in Mal.T_Array) return Mal.T;
|
||||
function Generic_Mal_Operator (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 2
|
||||
then raise Argument_Error with Name & ": expects 2 arguments"
|
||||
elsif (for some A of Args => A.Kind /= Kind_Number)
|
||||
then raise Argument_Error with Name & ": expects numbers"
|
||||
else (Kind_Number, Ada_Operator (Args (Args'First).Ada_Number,
|
||||
Args (Args'Last).Ada_Number)));
|
||||
|
||||
generic
|
||||
with function Ada_Operator (Left, Right : in Integer) return Boolean;
|
||||
function Generic_Comparison (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Generic_Comparison (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Boolean, Atoms.No_Element,
|
||||
Ada_Operator (Args (Args'First).Integer_Value,
|
||||
Args (Args'First + 1).Integer_Value));
|
||||
function Greater_Than is new Generic_Comparison (">");
|
||||
function Greater_Equal is new Generic_Comparison (">=");
|
||||
function Less_Than is new Generic_Comparison ("<");
|
||||
function Less_Equal is new Generic_Comparison ("<=");
|
||||
Name : in String;
|
||||
function Generic_Comparison (Args : in Mal.T_Array) return Mal.T;
|
||||
function Generic_Comparison (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 2
|
||||
then raise Argument_Error with Name & ": expects 2 arguments"
|
||||
elsif (for some A of Args => A.Kind /= Kind_Number)
|
||||
then raise Argument_Error with Name & ": expects numbers"
|
||||
else (Kind_Boolean, Ada_Operator (Args (Args'First).Ada_Number,
|
||||
Args (Args'Last).Ada_Number)));
|
||||
|
||||
generic
|
||||
Kind : Kind_Type;
|
||||
function Generic_Kind_Test (Args : in Mal_Type_Array) return Mal_Type;
|
||||
function Generic_Kind_Test (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Boolean, Atoms.No_Element, Args (Args'First).Kind = Kind);
|
||||
function Is_Atom is new Generic_Kind_Test (Kind_Atom);
|
||||
function Is_Keyword is new Generic_Kind_Test (Kind_Keyword);
|
||||
function Is_List is new Generic_Kind_Test (Kind_List);
|
||||
function Is_Map is new Generic_Kind_Test (Kind_Map);
|
||||
function Is_Nil is new Generic_Kind_Test (Kind_Nil);
|
||||
function Is_String is new Generic_Kind_Test (Kind_String);
|
||||
function Is_Symbol is new Generic_Kind_Test (Kind_Symbol);
|
||||
function Is_Vector is new Generic_Kind_Test (Kind_Vector);
|
||||
-- Built-in functions from this package.
|
||||
function Addition is new Generic_Mal_Operator ("+", "+");
|
||||
function Apply (Args : in Mal.T_Array) return Mal.T;
|
||||
function Division is new Generic_Mal_Operator ("/", "/");
|
||||
function Equals (Args : in Mal.T_Array) return Mal.T;
|
||||
function Eval (Args : in Mal.T_Array) return Mal.T;
|
||||
function Greater_Equal is new Generic_Comparison (">=", ">=");
|
||||
function Greater_Than is new Generic_Comparison (">", ">");
|
||||
function Is_Atom is new Generic_Kind_Test (Kind_Atom, "atom?");
|
||||
function Is_False (Args : in Mal.T_Array) return Mal.T;
|
||||
function Is_Function (Args : in Mal.T_Array) return Mal.T;
|
||||
function Is_Keyword is new Generic_Kind_Test (Kind_Keyword, "keyword?");
|
||||
function Is_List is new Generic_Kind_Test (Kind_List, "list?");
|
||||
function Is_Macro is new Generic_Kind_Test (Kind_Macro, "macro?");
|
||||
function Is_Map is new Generic_Kind_Test (Kind_Map, "map?");
|
||||
function Is_Nil is new Generic_Kind_Test (Kind_Nil, "nil?");
|
||||
function Is_Number is new Generic_Kind_Test (Kind_Number, "number?");
|
||||
function Is_Sequential (Args : in Mal.T_Array) return Mal.T;
|
||||
function Is_String is new Generic_Kind_Test (Kind_String, "string?");
|
||||
function Is_Symbol is new Generic_Kind_Test (Kind_Symbol, "symbol?");
|
||||
function Is_True (Args : in Mal.T_Array) return Mal.T;
|
||||
function Is_Vector is new Generic_Kind_Test (Kind_Vector, "vector?");
|
||||
function Keyword (Args : in Mal.T_Array) return Mal.T;
|
||||
function Less_Equal is new Generic_Comparison ("<=", "<=");
|
||||
function Less_Than is new Generic_Comparison ("<", "<");
|
||||
function Map (Args : in Mal.T_Array) return Mal.T;
|
||||
function Meta (Args : in Mal.T_Array) return Mal.T;
|
||||
function Pr_Str (Args : in Mal.T_Array) return Mal.T;
|
||||
function Println (Args : in Mal.T_Array) return Mal.T;
|
||||
function Prn (Args : in Mal.T_Array) return Mal.T;
|
||||
function Product is new Generic_Mal_Operator ("*", "*");
|
||||
function Read_String (Args : in Mal.T_Array) return Mal.T;
|
||||
function Readline (Args : in Mal.T_Array) return Mal.T;
|
||||
function Seq (Args : in Mal.T_Array) return Mal.T;
|
||||
function Slurp (Args : in Mal.T_Array) return Mal.T;
|
||||
function Str (Args : in Mal.T_Array) return Mal.T;
|
||||
function Subtraction is new Generic_Mal_Operator ("-", "-");
|
||||
function Swap (Args : in Mal.T_Array) return Mal.T;
|
||||
function Symbol (Args : in Mal.T_Array) return Mal.T;
|
||||
function Throw (Args : in Mal.T_Array) return Mal.T;
|
||||
function Time_Ms (Args : in Mal.T_Array) return Mal.T;
|
||||
function With_Meta (Args : in Mal.T_Array) return Mal.T;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
procedure Add_Built_In_Functions
|
||||
(Repl : in Environments.Ptr;
|
||||
Eval_Callback : in not null Eval_Callback_Type)
|
||||
is
|
||||
function N (N : in Native_Function_Access) return Mal_Type
|
||||
is (Kind_Native, Atoms.No_Element, N) with Inline;
|
||||
function Apply (Func : in Mal.T;
|
||||
Args : in Mal.T_Array;
|
||||
Name : in String)
|
||||
return Mal.T is
|
||||
begin
|
||||
Eval := Eval_Callback;
|
||||
|
||||
Repl.Increase_Capacity (57);
|
||||
|
||||
Repl.Set (Names.Apply, N (Apply'Access));
|
||||
Repl.Set (Names.Assoc, N (Assoc'Access));
|
||||
Repl.Set (Names.Asterisk, N (Product'Access));
|
||||
Repl.Set (Names.Atom, N (Atom'Access));
|
||||
Repl.Set (Names.Concat, N (Concat'Access));
|
||||
Repl.Set (Names.Conj, N (Conj'Access));
|
||||
Repl.Set (Names.Cons, N (Cons'Access));
|
||||
Repl.Set (Names.Contains, N (Contains'Access));
|
||||
Repl.Set (Names.Count, N (Count'Access));
|
||||
Repl.Set (Names.Deref, N (Deref'Access));
|
||||
Repl.Set (Names.Dissoc, N (Dissoc'Access));
|
||||
Repl.Set (Names.Equals, N (Equals'Access));
|
||||
Repl.Set (Names.First, N (First'Access));
|
||||
Repl.Set (Names.Get, N (Get'Access));
|
||||
Repl.Set (Names.Greater_Equal, N (Greater_Equal'Access));
|
||||
Repl.Set (Names.Greater_Than, N (Greater_Than'Access));
|
||||
Repl.Set (Names.Hash_Map, N (Hash_Map'Access));
|
||||
Repl.Set (Names.Is_Atom, N (Is_Atom'Access));
|
||||
Repl.Set (Names.Is_Empty, N (Is_Empty'Access));
|
||||
Repl.Set (Names.Is_False, N (Is_False'Access));
|
||||
Repl.Set (Names.Is_Keyword, N (Is_Keyword'Access));
|
||||
Repl.Set (Names.Is_List, N (Is_List'Access));
|
||||
Repl.Set (Names.Is_Map, N (Is_Map'Access));
|
||||
Repl.Set (Names.Is_Nil, N (Is_Nil'Access));
|
||||
Repl.Set (Names.Is_Sequential, N (Is_Sequential'Access));
|
||||
Repl.Set (Names.Is_String, N (Is_String'Access));
|
||||
Repl.Set (Names.Is_Symbol, N (Is_Symbol'Access));
|
||||
Repl.Set (Names.Is_True, N (Is_True'Access));
|
||||
Repl.Set (Names.Is_Vector, N (Is_Vector'Access));
|
||||
Repl.Set (Names.Keys, N (Keys'Access));
|
||||
Repl.Set (Names.Keyword, N (Keyword'Access));
|
||||
Repl.Set (Names.Less_Equal, N (Less_Equal'Access));
|
||||
Repl.Set (Names.Less_Than, N (Less_Than'Access));
|
||||
Repl.Set (Names.List, N (List'Access));
|
||||
Repl.Set (Names.Map, N (Map'Access));
|
||||
Repl.Set (Names.Meta, N (Meta'Access));
|
||||
Repl.Set (Names.Minus, N (Subtraction'Access));
|
||||
Repl.Set (Names.Nth, N (Nth'Access));
|
||||
Repl.Set (Names.Plus, N (Addition'Access));
|
||||
Repl.Set (Names.Pr_Str, N (Pr_Str'Access));
|
||||
Repl.Set (Names.Println, N (Println'Access));
|
||||
Repl.Set (Names.Prn, N (Prn'Access));
|
||||
Repl.Set (Names.Read_String, N (Read_String'Access));
|
||||
Repl.Set (Names.Readline, N (Readline'Access));
|
||||
Repl.Set (Names.Reset, N (Reset'Access));
|
||||
Repl.Set (Names.Rest, N (Rest'Access));
|
||||
Repl.Set (Names.Seq, N (Seq'Access));
|
||||
Repl.Set (Names.Slash, N (Division'Access));
|
||||
Repl.Set (Names.Slurp, N (Slurp'Access));
|
||||
Repl.Set (Names.Str, N (Str'Access));
|
||||
Repl.Set (Names.Swap, N (Swap'Access));
|
||||
Repl.Set (Names.Symbol, N (Symbol'Access));
|
||||
Repl.Set (Names.Throw, N (Throw'Access));
|
||||
Repl.Set (Names.Time_Ms, N (Time_Ms'Access));
|
||||
Repl.Set (Names.Vals, N (Vals'Access));
|
||||
Repl.Set (Names.Vector, N (Vector'Access));
|
||||
Repl.Set (Names.With_Meta, N (With_Meta'Access));
|
||||
end Add_Built_In_Functions;
|
||||
|
||||
function Apply (Args : in Mal_Type_Array) return Mal_Type
|
||||
is
|
||||
Func : Mal_Type renames Args (Args'First);
|
||||
List : Lists.Ptr renames Args (Args'Last).L;
|
||||
Actuals : Mal_Type_Array (1 .. Args'Length - 2 + List.Length);
|
||||
begin
|
||||
Actuals (1 .. Args'Length - 2) := Args (Args'First + 1 .. Args'Last - 1);
|
||||
for I in 1 .. List.Length loop
|
||||
Actuals (Args'Length - 2 + I) := List.Element (I);
|
||||
end loop;
|
||||
if Func.Kind = Kind_Native then
|
||||
return Func.Native.all (Actuals);
|
||||
else
|
||||
case Func.Kind is
|
||||
when Kind_Builtin =>
|
||||
return Func.Builtin.all (Args);
|
||||
when Kind_Builtin_With_Meta =>
|
||||
return Func.Builtin_With_Meta.Data.all (Args);
|
||||
when Kind_Function =>
|
||||
declare
|
||||
Env : constant Environments.Ptr
|
||||
:= Environments.Alloc (Outer => Func.Environment);
|
||||
Env : constant Environments.Ptr := Func.Function_Value.Closure.Sub;
|
||||
begin
|
||||
Env.Set_Binds (Func.Formals, Actuals);
|
||||
return Eval.all (Func.Expression.Deref, Env);
|
||||
Func.Function_Value.Set_Binds (Env, Args);
|
||||
return Eval_Ref.all (Func.Function_Value.Expression, Env);
|
||||
end;
|
||||
end if;
|
||||
when others =>
|
||||
raise Argument_Error with Name & ": cannot execute "
|
||||
& ASU.To_String (Printer.Pr_Str (Func));
|
||||
end case;
|
||||
end Apply;
|
||||
|
||||
function Assoc (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Map, Atoms.No_Element,
|
||||
Args (Args'First).Map.Assoc (Args (Args'First + 1 .. Args'Last)));
|
||||
function Apply (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length < 2 then
|
||||
raise Argument_Error with "apply: expects at least 2 arguments"
|
||||
elsif Args (Args'Last).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "apply: last arg must a be list or vector"
|
||||
else
|
||||
Apply (Args (Args'First),
|
||||
Args (Args'First + 1 .. Args'Last - 1) & Args (Args'Last).L,
|
||||
"apply"));
|
||||
|
||||
function Atom (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Atom, Atoms.No_Element, Atoms.Alloc (Args (Args'First)));
|
||||
function Equals (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 2 then
|
||||
raise Argument_Error with "=: expects 2 arguments"
|
||||
else
|
||||
(Kind_Boolean, Args (Args'First) = Args (Args'Last)));
|
||||
|
||||
function Concat (Args : in Mal_Type_Array) return Mal_Type
|
||||
is
|
||||
L : array (Args'Range) of Lists.Ptr;
|
||||
Sum : Natural := 0;
|
||||
Result : Lists.Ptr;
|
||||
function Eval (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "eval: expects 1 argument"
|
||||
else
|
||||
(Eval_Ref.all (Args (Args'First), Environments.Repl)));
|
||||
|
||||
function Is_False (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "false?: expects 1 argument"
|
||||
else (Kind_Boolean, Args (Args'First).Kind = Kind_Boolean
|
||||
and then not Args (Args'First).Ada_Boolean));
|
||||
|
||||
function Is_Function (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "count: expects 1 argument"
|
||||
else
|
||||
(Kind_Boolean, Args (Args'First).Kind in
|
||||
Kind_Function | Kind_Builtin | Kind_Builtin_With_Meta));
|
||||
|
||||
function Is_Sequential (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "sequential?: expects 1 argument"
|
||||
else
|
||||
(Kind_Boolean, Args (Args'First).Kind in Kind_List | Kind_Vector));
|
||||
|
||||
function Is_True (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "true?: expects 1 argument"
|
||||
else
|
||||
(Kind_Boolean, Args (Args'First).Kind = Kind_Boolean
|
||||
and then Args (Args'First).Ada_Boolean));
|
||||
|
||||
function Keyword (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "keyword: expects 1 argument"
|
||||
elsif Args (Args'First).Kind not in Kind_Keyword | Kind_String then
|
||||
raise Argument_Error with "keyword: expects a keyword or a string"
|
||||
else
|
||||
(Kind_Keyword, Args (Args'First).S));
|
||||
|
||||
function Map (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
L (I) := Args (I).L;
|
||||
Sum := Sum + L (I).Length;
|
||||
end loop;
|
||||
Result := Lists.Alloc (Sum);
|
||||
Sum := 0;
|
||||
for LI of L loop
|
||||
for J in 1 .. LI.Length loop
|
||||
Sum := Sum + 1;
|
||||
Result.Replace_Element (Sum, LI.Element (J));
|
||||
end loop;
|
||||
end loop;
|
||||
return (Kind_List, Atoms.No_Element, Result);
|
||||
end Concat;
|
||||
|
||||
function Concatenation_Of_Pr_Str
|
||||
(Args : in Mal_Type_Array;
|
||||
Print_Readably : in Boolean := True;
|
||||
Separator : in String := " ")
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
is
|
||||
use Ada.Strings.Unbounded;
|
||||
Result : Unbounded_String;
|
||||
begin
|
||||
if 1 <= Args'Length then
|
||||
Append (Result, Printer.Pr_Str (Args (Args'First), Print_Readably));
|
||||
for I in Args'First + 1 .. Args'Last loop
|
||||
Append (Result, Separator);
|
||||
Append (Result, Printer.Pr_Str (Args (I), Print_Readably));
|
||||
end loop;
|
||||
if Args'Length /= 2 then
|
||||
raise Argument_Error with "map: expects 2 arguments";
|
||||
elsif Args (Args'Last).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "map: arg 2 must be a list or vector";
|
||||
end if;
|
||||
return Result;
|
||||
end Concatenation_Of_Pr_Str;
|
||||
|
||||
function Conj (Args : in Mal_Type_Array) return Mal_Type
|
||||
is
|
||||
List : Lists.Ptr renames Args (Args'First).L;
|
||||
Result : constant Lists.Ptr
|
||||
:= Lists.Alloc (List.Length + Args'Length - 1);
|
||||
begin
|
||||
if Args (Args'First).Kind = Kind_List then
|
||||
for I in Args'First + 1 .. Args'Last loop
|
||||
Result.Replace_Element (Args'Last + 1 - I, Args (I));
|
||||
end loop;
|
||||
for I in 1 .. List.Length loop
|
||||
Result.Replace_Element (Args'Length + I - 1, List.Element (I));
|
||||
end loop;
|
||||
return (Kind_List, Atoms.No_Element, Result);
|
||||
else
|
||||
for I in 1 .. Args'Length - 1 loop
|
||||
Result.Replace_Element (List.Length + I, Args (Args'First + I));
|
||||
end loop;
|
||||
for I in 1 .. List.Length loop
|
||||
Result.Replace_Element (I, List.Element (I));
|
||||
end loop;
|
||||
return (Kind_Vector, Atoms.No_Element, Result);
|
||||
end if;
|
||||
end Conj;
|
||||
|
||||
function Cons (Args : in Mal_Type_Array) return Mal_Type
|
||||
is
|
||||
List : Lists.Ptr renames Args (Args'First + 1).L;
|
||||
Result : constant Lists.Ptr := Lists.Alloc (1 + List.Length);
|
||||
begin
|
||||
Result.Replace_Element (1, Args (Args'First));
|
||||
for I in 1 .. List.Length loop
|
||||
Result.Replace_Element (I + 1, List.Element (I));
|
||||
end loop;
|
||||
return (Kind_List, Atoms.No_Element, Result);
|
||||
end Cons;
|
||||
|
||||
function Contains (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Boolean, Atoms.No_Element,
|
||||
Args (Args'First).Map.Contains (Args (Args'First + 1)));
|
||||
|
||||
function Count (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Number, Atoms.No_Element,
|
||||
(if Args (Args'First).Kind = Kind_Nil
|
||||
then 0
|
||||
else Args (Args'First).L.Length));
|
||||
|
||||
function Deref (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Args (Args'First).Reference.Deref);
|
||||
|
||||
function Dissoc (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Map, Atoms.No_Element,
|
||||
Args (Args'First).Map.Dissoc (Args (Args'First + 1 .. Args'Last)));
|
||||
|
||||
function Equals (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Boolean, Atoms.No_Element,
|
||||
Args (Args'First) = Args (Args'First + 1));
|
||||
|
||||
function First (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (if Args (Args'First).Kind = Kind_Nil
|
||||
or else Args (Args'First).L.Length = 0
|
||||
then (Kind_Nil, Atoms.No_Element)
|
||||
else Args (Args'First).L.Element (1));
|
||||
|
||||
function Get (Args : in Mal_Type_Array) return Mal_Type is
|
||||
begin
|
||||
if Args (Args'First).Kind = Kind_Nil then
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
else
|
||||
return Args (Args'First).Map.Get (Args (Args'First + 1));
|
||||
end if;
|
||||
exception
|
||||
when Maps.Unknown_Key =>
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
end Get;
|
||||
|
||||
function Hash_Map (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Map, Atoms.No_Element, Maps.Hash_Map (Args));
|
||||
|
||||
function Is_Empty (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Boolean, Atoms.No_Element, Args (Args'First).L.Length = 0);
|
||||
|
||||
function Is_False (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Boolean, Atoms.No_Element,
|
||||
Args (Args'First).Kind = Kind_Boolean
|
||||
and then not Args (Args'First).Boolean_Value);
|
||||
|
||||
function Is_True (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Boolean, Atoms.No_Element,
|
||||
Args (Args'First).Kind = Kind_Boolean
|
||||
and then Args (Args'First).Boolean_Value);
|
||||
|
||||
function Is_Sequential (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Boolean, Atoms.No_Element,
|
||||
Args (Args'First).Kind in Kind_List | Kind_Vector);
|
||||
|
||||
function Keyword (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Keyword, Atoms.No_Element, Args (Args'First).S);
|
||||
|
||||
function Keys (Args : in Mal_Type_Array) return Mal_Type
|
||||
is
|
||||
M : Maps.Ptr renames Args (Args'First).Map;
|
||||
Result : constant Mal_Type := (Kind_List, Atoms.No_Element,
|
||||
Lists.Alloc (M.Length));
|
||||
I : Natural := 0;
|
||||
procedure Process (Key, Element : in Mal_Type);
|
||||
procedure Process (Key, Element : in Mal_Type) is
|
||||
declare
|
||||
R : Mal.T_Array (1 .. Args (Args'Last).L.Length);
|
||||
begin
|
||||
I := I + 1;
|
||||
Result.L.Replace_Element (I, Key);
|
||||
pragma Unreferenced (Element);
|
||||
end Process;
|
||||
begin
|
||||
M.Iterate (Process'Access);
|
||||
return Result;
|
||||
end Keys;
|
||||
|
||||
function List (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_List, Atoms.No_Element, Lists.Alloc (Args));
|
||||
|
||||
function Map (Args : in Mal_Type_Array) return Mal_Type
|
||||
is
|
||||
Func : Mal_Type renames Args (Args'First);
|
||||
List : Lists.Ptr renames Args (Args'First + 1).L;
|
||||
Actuals : Mal_Type_Array (1 .. 1);
|
||||
Result : constant Lists.Ptr := Lists.Alloc (List.Length);
|
||||
begin
|
||||
for I in 1 .. List.Length loop
|
||||
Actuals (1) := List.Element (I);
|
||||
if Func.Kind = Kind_Native then
|
||||
Result.Replace_Element (I, Func.Native.all (Actuals));
|
||||
else
|
||||
declare
|
||||
Env : constant Environments.Ptr
|
||||
:= Environments.Alloc (Func.Environment);
|
||||
begin
|
||||
Env.Set_Binds (Func.Formals, Actuals);
|
||||
Result.Replace_Element (I, Eval.all (Func.Expression.Deref,
|
||||
Env));
|
||||
end;
|
||||
end if;
|
||||
end loop;
|
||||
return (Kind_List, Atoms.No_Element, Result);
|
||||
for I in R'Range loop
|
||||
R (I) := Apply (Args (Args'First),
|
||||
Mal.T_Array'(1 => Args (Args'Last).L.Element (I)),
|
||||
"map");
|
||||
end loop;
|
||||
return Lists.List (R);
|
||||
end;
|
||||
end Map;
|
||||
|
||||
function Meta (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (if Args (Args'First).Meta = Atoms.No_Element
|
||||
then (Kind_Nil, Atoms.No_Element)
|
||||
else Args (Args'First).Meta.Deref);
|
||||
function Meta (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "meta: expects 1 argument"
|
||||
else
|
||||
(case Args (Args'First).Kind is
|
||||
when Kind_List | Kind_Vector =>
|
||||
Args (Args'First).L.Meta,
|
||||
when Kind_Map =>
|
||||
Args (Args'First).Map.Meta,
|
||||
when Kind_Function =>
|
||||
Args (Args'First).Function_Value.Meta,
|
||||
when Kind_Builtin_With_Meta =>
|
||||
Args (Args'First).Builtin_With_Meta.Meta,
|
||||
when others =>
|
||||
Mal.Nil));
|
||||
|
||||
function Nth (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Args (Args'First).L.Element (1 + Args (Args'First + 1).Integer_Value));
|
||||
|
||||
function Pr_Str (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_String, Atoms.No_Element, Strings.Alloc
|
||||
(Ada.Strings.Unbounded.To_String (Concatenation_Of_Pr_Str (Args))));
|
||||
|
||||
function Println (Args : in Mal_Type_Array) return Mal_Type is
|
||||
function Pr_Str (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
Ada.Text_IO.Unbounded_IO.Put_Line (Concatenation_Of_Pr_Str
|
||||
(Args, Print_Readably => False));
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
end Println;
|
||||
|
||||
function Prn (Args : in Mal_Type_Array) return Mal_Type is
|
||||
begin
|
||||
Ada.Text_IO.Unbounded_IO.Put_Line (Concatenation_Of_Pr_Str (Args));
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
end Prn;
|
||||
|
||||
function Readline (Args : in Mal_Type_Array) return Mal_Type is
|
||||
begin
|
||||
Ada.Text_IO.Put (Args (Args'First).S.Deref);
|
||||
return (Kind_String, Atoms.No_Element,
|
||||
Strings.Alloc (Ada.Text_IO.Get_Line));
|
||||
exception
|
||||
when Ada.Text_IO.End_Error =>
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
end Readline;
|
||||
|
||||
function Read_String (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Reader.Read_Str (Args (Args'First).S.Deref));
|
||||
|
||||
function Reset (Args : in Mal_Type_Array) return Mal_Type is
|
||||
begin
|
||||
Args (Args'First).Reference.Set (Args (Args'Last));
|
||||
return Args (Args'Last);
|
||||
end Reset;
|
||||
|
||||
function Rest (Args : in Mal_Type_Array) return Mal_Type
|
||||
is
|
||||
List : Mal_Type renames Args (Args'First);
|
||||
Len : Natural;
|
||||
begin
|
||||
return Result : Mal_Type (Kind_List) do
|
||||
if List.Kind /= Kind_Nil then
|
||||
Len := List.L.Length;
|
||||
if 0 < Len then
|
||||
Len := Len - 1;
|
||||
Result.L := Lists.Alloc (Len);
|
||||
for I in 1 .. Len loop
|
||||
Result.L.Replace_Element (I, List.L.Element (I + 1));
|
||||
end loop;
|
||||
end if;
|
||||
return R : Mal.T := (Kind_String, ASU.Null_Unbounded_String) do
|
||||
if 0 < Args'Length then
|
||||
ASU.Append (R.S, Printer.Pr_Str (Args (Args'First)));
|
||||
for I in Args'First + 1 .. Args'Last loop
|
||||
ASU.Append (R.S, ' ');
|
||||
ASU.Append (R.S, Printer.Pr_Str (Args (I)));
|
||||
end loop;
|
||||
end if;
|
||||
end return;
|
||||
end Rest;
|
||||
end Pr_Str;
|
||||
|
||||
function Seq (Args : in Mal_Type_Array) return Mal_Type is
|
||||
function Println (Args : in Mal.T_Array) return Mal.T is
|
||||
use Ada.Text_IO.Unbounded_IO;
|
||||
begin
|
||||
if Args (Args'First).Kind = Kind_String then
|
||||
declare
|
||||
S : constant String := Args (Args'First).S.Deref;
|
||||
Result : Lists.Ptr;
|
||||
begin
|
||||
if S'Length = 0 then
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
else
|
||||
Result := Lists.Alloc (S'Length);
|
||||
for I in S'Range loop
|
||||
Result.Replace_Element (I - S'First + 1, Mal_Type'
|
||||
(Kind_String, Atoms.No_Element,
|
||||
Strings.Alloc (S (I .. I))));
|
||||
end loop;
|
||||
return (Kind_List, Atoms.No_Element, Result);
|
||||
end if;
|
||||
end;
|
||||
elsif Args (Args'First).Kind = Kind_Nil
|
||||
or else Args (Args'First).L.Length = 0
|
||||
then
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
else
|
||||
return (Kind_List, Atoms.No_Element, Args (Args'First).L);
|
||||
if 0 < Args'Length then
|
||||
Put (Printer.Pr_Str (Args (Args'First), Readably => False));
|
||||
for I in Args'First + 1 .. Args'Last loop
|
||||
Ada.Text_IO.Put (' ');
|
||||
Put (Printer.Pr_Str (Args (I), Readably => False));
|
||||
end loop;
|
||||
end if;
|
||||
Ada.Text_IO.New_Line;
|
||||
return Mal.Nil;
|
||||
end Println;
|
||||
|
||||
function Prn (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
if 0 < Args'Length then
|
||||
Ada.Text_IO.Unbounded_IO.Put (Printer.Pr_Str (Args (Args'First)));
|
||||
for I in Args'First + 1 .. Args'Last loop
|
||||
Ada.Text_IO.Put (' ');
|
||||
Ada.Text_IO.Unbounded_IO.Put (Printer.Pr_Str (Args (I)));
|
||||
end loop;
|
||||
end if;
|
||||
Ada.Text_IO.New_Line;
|
||||
return Mal.Nil;
|
||||
end Prn;
|
||||
|
||||
function Readline (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
if Args'Length /= 1 then
|
||||
raise Argument_Error with "readline: expects 1 argument";
|
||||
elsif Args (Args'First).Kind not in Kind_Keyword | Kind_String then
|
||||
raise Argument_Error with "readline: expects a keyword or string";
|
||||
else
|
||||
Ada.Text_IO.Unbounded_IO.Put (Args (Args'First).S);
|
||||
return (Kind_String, Ada.Text_IO.Unbounded_IO.Get_Line);
|
||||
end if;
|
||||
exception
|
||||
when Ada.Text_IO.End_Error =>
|
||||
return Mal.Nil;
|
||||
end Readline;
|
||||
|
||||
function Read_String (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "read-string: expects 1 argument"
|
||||
elsif Args (Args'First).Kind /= Kind_String then
|
||||
raise Argument_Error with "read-string: expects a string"
|
||||
else
|
||||
Reader.Read_Str (ASU.To_String (Args (Args'First).S)));
|
||||
|
||||
function Seq (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
if Args'Length /= 1 then
|
||||
raise Argument_Error with "seq: expects 1 argument";
|
||||
end if;
|
||||
case Args (Args'First).Kind is
|
||||
when Kind_Nil =>
|
||||
return Mal.Nil;
|
||||
when Kind_String =>
|
||||
if ASU.Length (Args (Args'First).S) = 0 then
|
||||
return Mal.Nil;
|
||||
else
|
||||
declare
|
||||
A1 : constant ASU.Unbounded_String := Args (Args'First).S;
|
||||
R : Mal.T_Array (1 .. ASU.Length (A1));
|
||||
begin
|
||||
for I in R'Range loop
|
||||
R (I) := (Kind_String, ASU.Unbounded_Slice (A1, I, I));
|
||||
end loop;
|
||||
return Lists.List (R);
|
||||
end;
|
||||
end if;
|
||||
when Kind_List | Kind_Vector =>
|
||||
if Args (Args'First).L.Length = 0 then
|
||||
return Mal.Nil;
|
||||
else
|
||||
return (Kind_List, Args (Args'First).L);
|
||||
end if;
|
||||
when others =>
|
||||
raise Argument_Error with "seq: expects a string, list or vector";
|
||||
end case;
|
||||
end Seq;
|
||||
|
||||
function Slurp (Args : in Mal_Type_Array) return Mal_Type
|
||||
is
|
||||
use Ada.Strings.Unbounded;
|
||||
function Slurp (Args : in Mal.T_Array) return Mal.T is
|
||||
use Ada.Text_IO;
|
||||
File : File_Type;
|
||||
Buffer : Unbounded_String;
|
||||
Buffer : ASU.Unbounded_String;
|
||||
begin
|
||||
Open (File, In_File, Args (Args'First).S.Deref);
|
||||
while not End_Of_File (File) loop
|
||||
Append (Buffer, Get_Line (File));
|
||||
Append (Buffer, Ada.Characters.Latin_1.LF);
|
||||
end loop;
|
||||
Close (File);
|
||||
return (Kind_String, Atoms.No_Element,
|
||||
Strings.Alloc (To_String (Buffer)));
|
||||
if Args'Length /= 1 then
|
||||
raise Argument_Error with "slurp: expects 1 argument";
|
||||
elsif Args (Args'First).Kind /= Kind_String then
|
||||
raise Argument_Error with "slurp: expects a string";
|
||||
else
|
||||
Open (File, In_File, ASU.To_String (Args (Args'First).S));
|
||||
while not End_Of_File (File) loop
|
||||
ASU.Append (Buffer, Get_Line (File));
|
||||
ASU.Append (Buffer, Ada.Characters.Latin_1.LF);
|
||||
end loop;
|
||||
Close (File);
|
||||
return (Kind_String, Buffer);
|
||||
end if;
|
||||
exception
|
||||
when others =>
|
||||
Close (File);
|
||||
raise;
|
||||
end Slurp;
|
||||
|
||||
function Str (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_String, Atoms.No_Element, Strings.Alloc
|
||||
(Ada.Strings.Unbounded.To_String
|
||||
(Concatenation_Of_Pr_Str (Args,
|
||||
Print_Readably => False,
|
||||
Separator => ""))));
|
||||
|
||||
function Swap (Args : in Mal_Type_Array) return Mal_Type
|
||||
is
|
||||
Atom : Mal_Type renames Args (Args'First);
|
||||
Func : Mal_Type renames Args (Args'First + 1);
|
||||
Actuals : Mal_Type_Array (Args'First + 1 .. Args'Last);
|
||||
Result : Mal_Type;
|
||||
function Str (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
Actuals (Actuals'First) := Atom.Reference.Deref;
|
||||
for I in Actuals'First + 1 .. Args'Last loop
|
||||
Actuals (I) := Args (I);
|
||||
end loop;
|
||||
if Func.Kind = Kind_Native then
|
||||
Result := Func.Native.all (Actuals);
|
||||
else
|
||||
declare
|
||||
Env : constant Environments.Ptr
|
||||
:= Environments.Alloc (Outer => Func.Environment);
|
||||
begin
|
||||
Env.Set_Binds (Func.Formals, Actuals);
|
||||
Result := Eval.all (Func.Expression.Deref, Env);
|
||||
end;
|
||||
return R : Mal.T := (Kind_String, ASU.Null_Unbounded_String) do
|
||||
for Arg of Args loop
|
||||
ASU.Append (R.S, Printer.Pr_Str (Arg, Readably => False));
|
||||
end loop;
|
||||
end return;
|
||||
end Str;
|
||||
|
||||
function Swap (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
if Args'Length < 2 then
|
||||
raise Argument_Error with "swap!: expects at least 2 arguments";
|
||||
elsif Args (Args'First).Kind /= Kind_Atom then
|
||||
raise Argument_Error with "swap!: arg 1 must be an atom";
|
||||
end if;
|
||||
Atom.Reference.Set (Result);
|
||||
return Result;
|
||||
declare
|
||||
X : Mal.T renames Atoms.Deref (Args (Args'First .. Args'First));
|
||||
FX : Mal.T renames Apply (Args (Args'First + 1),
|
||||
X & Args (Args'First + 2 .. Args'Last),
|
||||
"swap!");
|
||||
begin
|
||||
return Atoms.Reset (Mal.T_Array'(Args (Args'First), FX));
|
||||
end;
|
||||
end Swap;
|
||||
|
||||
function Symbol (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Symbol, Atoms.No_Element, Args (Args'First).S);
|
||||
function Symbol (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "symbol?: expects 1 argument"
|
||||
else
|
||||
(Kind_Symbol,
|
||||
Symbols.Constructor (ASU.To_String (Args (Args'First).S))));
|
||||
|
||||
function Throw (Args : in Mal_Type_Array) return Mal_Type is
|
||||
function Throw (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
Last_Exception := Args (Args'First);
|
||||
raise Exception_Throwed;
|
||||
return (Kind_Nil, Atoms.No_Element); -- GNAT wants a return.
|
||||
if Args'Length /= 1 then
|
||||
raise Argument_Error with "throw: expects 1 argument";
|
||||
else
|
||||
Last_Exception := Args (Args'First);
|
||||
raise Exception_Throwed;
|
||||
return Mal.Nil; -- GNAT wants a return.
|
||||
end if;
|
||||
end Throw;
|
||||
|
||||
function Time_Ms (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Number, Atoms.No_Element,
|
||||
Integer (1000.0 * (Ada.Calendar.Clock - Start_Time)));
|
||||
function Time_Ms (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 0 then
|
||||
raise Argument_Error with "time: expects no argument"
|
||||
else
|
||||
(Kind_Number, Integer (1000.0 * (Ada.Calendar.Clock - Start_Time))));
|
||||
|
||||
function Vals (Args : in Mal_Type_Array) return Mal_Type
|
||||
is
|
||||
M : Maps.Ptr renames Args (Args'First).Map;
|
||||
Result : constant Mal_Type := (Kind_List, Atoms.No_Element,
|
||||
Lists.Alloc (M.Length));
|
||||
I : Natural := 0;
|
||||
procedure Process (Key, Element : in Mal_Type);
|
||||
procedure Process (Key, Element : in Mal_Type) is
|
||||
begin
|
||||
I := I + 1;
|
||||
Result.L.Replace_Element (I, Element);
|
||||
pragma Unreferenced (Key);
|
||||
end Process;
|
||||
begin
|
||||
M.Iterate (Process'Access);
|
||||
return Result;
|
||||
end Vals;
|
||||
|
||||
function Vector (Args : in Mal_Type_Array) return Mal_Type
|
||||
is (Kind_Vector, Atoms.No_Element, Lists.Alloc (Args));
|
||||
|
||||
function With_Meta (Args : in Mal_Type_Array) return Mal_Type is
|
||||
begin
|
||||
return Result : Mal_Type := Args (Args'First) do
|
||||
Result.Meta := Atoms.Alloc (Args (Args'First + 1));
|
||||
end return;
|
||||
end With_Meta;
|
||||
function With_Meta (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 2 then
|
||||
raise Argument_Error with "with-meta: expects 2 arguments"
|
||||
else (case Args (Args'First).Kind is
|
||||
when Kind_Builtin_With_Meta =>
|
||||
Args (Args'First).Builtin_With_Meta.With_Meta (Args (Args'Last)),
|
||||
when Kind_Builtin =>
|
||||
Builtins.With_Meta (Args (Args'First).Builtin, Args (Args'Last)),
|
||||
when Kind_List =>
|
||||
(Kind_List, Args (Args'First).L.With_Meta (Args (Args'Last))),
|
||||
when Kind_Vector =>
|
||||
(Kind_Vector, Args (Args'First).L.With_Meta (Args (Args'Last))),
|
||||
when Kind_Map =>
|
||||
Args (Args'First).Map.With_Meta (Args (Args'Last)),
|
||||
when Kind_Function =>
|
||||
Args (Args'First).Function_Value.With_Meta (Args (Args'Last)),
|
||||
when others =>
|
||||
Args (Args'First)));
|
||||
|
||||
use Symbols;
|
||||
R : Environments.Ptr renames Environments.Repl;
|
||||
B : Kind_Type renames Kind_Builtin;
|
||||
begin
|
||||
R.Set (Constructor ("+"), (B, Addition'Access));
|
||||
R.Set (Constructor ("apply"), (B, Apply'Access));
|
||||
R.Set (Constructor ("assoc"), (B, Maps.Assoc'Access));
|
||||
R.Set (Constructor ("atom"), (B, Atoms.Atom'Access));
|
||||
R.Set (Constructor ("concat"), (B, Lists.Concat'Access));
|
||||
R.Set (Constructor ("conj"), (B, Lists.Conj'Access));
|
||||
R.Set (Constructor ("cons"), (B, Lists.Cons'Access));
|
||||
R.Set (Constructor ("contains?"), (B, Maps.Contains'Access));
|
||||
R.Set (Constructor ("count"), (B, Lists.Count'Access));
|
||||
R.Set (Names.Deref, (B, Atoms.Deref'Access));
|
||||
R.Set (Constructor ("dissoc"), (B, Maps.Dissoc'Access));
|
||||
R.Set (Constructor ("/"), (B, Division'Access));
|
||||
R.Set (Constructor ("="), (B, Equals'Access));
|
||||
R.Set (Constructor ("eval"), (B, Eval'Access));
|
||||
R.Set (Constructor ("first"), (B, Lists.First'Access));
|
||||
R.Set (Constructor ("get"), (B, Maps.Get'Access));
|
||||
R.Set (Constructor (">="), (B, Greater_Equal'Access));
|
||||
R.Set (Constructor (">"), (B, Greater_Than'Access));
|
||||
R.Set (Constructor ("hash-map"), (B, Maps.Hash_Map'Access));
|
||||
R.Set (Constructor ("atom?"), (B, Is_Atom'Access));
|
||||
R.Set (Constructor ("empty?"), (B, Lists.Is_Empty'Access));
|
||||
R.Set (Constructor ("false?"), (B, Is_False'Access));
|
||||
R.Set (Constructor ("fn?"), (B, Is_Function'Access));
|
||||
R.Set (Constructor ("keyword?"), (B, Is_Keyword'Access));
|
||||
R.Set (Constructor ("list?"), (B, Is_List'Access));
|
||||
R.Set (Constructor ("macro?"), (B, Is_Macro'Access));
|
||||
R.Set (Constructor ("map?"), (B, Is_Map'Access));
|
||||
R.Set (Constructor ("nil?"), (B, Is_Nil'Access));
|
||||
R.Set (Constructor ("number?"), (B, Is_Number'Access));
|
||||
R.Set (Constructor ("sequential?"), (B, Is_Sequential'Access));
|
||||
R.Set (Constructor ("string?"), (B, Is_String'Access));
|
||||
R.Set (Constructor ("symbol?"), (B, Is_Symbol'Access));
|
||||
R.Set (Constructor ("true?"), (B, Is_True'Access));
|
||||
R.Set (Constructor ("vector?"), (B, Is_Vector'Access));
|
||||
R.Set (Constructor ("keys"), (B, Maps.Keys'Access));
|
||||
R.Set (Constructor ("keyword"), (B, Keyword'Access));
|
||||
R.Set (Constructor ("<="), (B, Less_Equal'Access));
|
||||
R.Set (Constructor ("<"), (B, Less_Than'Access));
|
||||
R.Set (Constructor ("list"), (B, Lists.List'Access));
|
||||
R.Set (Constructor ("map"), (B, Map'Access));
|
||||
R.Set (Constructor ("meta"), (B, Meta'Access));
|
||||
R.Set (Constructor ("nth"), (B, Lists.Nth'Access));
|
||||
R.Set (Constructor ("pr-str"), (B, Pr_Str'Access));
|
||||
R.Set (Constructor ("println"), (B, Println'Access));
|
||||
R.Set (Constructor ("prn"), (B, Prn'Access));
|
||||
R.Set (Constructor ("*"), (B, Product'Access));
|
||||
R.Set (Constructor ("read-string"), (B, Read_String'Access));
|
||||
R.Set (Constructor ("readline"), (B, Readline'Access));
|
||||
R.Set (Constructor ("reset!"), (B, Atoms.Reset'Access));
|
||||
R.Set (Constructor ("rest"), (B, Lists.Rest'Access));
|
||||
R.Set (Constructor ("seq"), (B, Seq'Access));
|
||||
R.Set (Constructor ("slurp"), (B, Slurp'Access));
|
||||
R.Set (Constructor ("str"), (B, Str'Access));
|
||||
R.Set (Constructor ("-"), (B, Subtraction'Access));
|
||||
R.Set (Constructor ("swap!"), (B, Swap'Access));
|
||||
R.Set (Constructor ("symbol"), (B, Symbol'Access));
|
||||
R.Set (Constructor ("throw"), (B, Throw'Access));
|
||||
R.Set (Constructor ("time-ms"), (B, Time_Ms'Access));
|
||||
R.Set (Constructor ("vals"), (B, Maps.Vals'Access));
|
||||
R.Set (Constructor ("vector"), (B, Lists.Vector'Access));
|
||||
R.Set (Names.With_Meta, (B, With_Meta'Access));
|
||||
end Core;
|
||||
|
@ -1,17 +1,19 @@
|
||||
with Environments;
|
||||
with Types; pragma Elaborate_All (Types);
|
||||
limited with Environments;
|
||||
with Types.Mal;
|
||||
|
||||
package Core is
|
||||
package Core with Elaborate_Body is
|
||||
|
||||
type Eval_Callback_Type is access
|
||||
function (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
-- Initialization of this package fills Environments.Repl with
|
||||
-- built-in functions.
|
||||
|
||||
procedure Add_Built_In_Functions
|
||||
(Repl : in Environments.Ptr;
|
||||
Eval_Callback : in not null Eval_Callback_Type);
|
||||
Eval_Ref : access function (Ast : in Types.Mal.T;
|
||||
Env : in Environments.Ptr)
|
||||
return Types.Mal.T;
|
||||
-- Set by the main program at startup.
|
||||
|
||||
Exception_Throwed : exception;
|
||||
Last_Exception : Types.Mal_Type;
|
||||
Last_Exception : Types.Mal.T;
|
||||
-- When the exception is throwed, Last_Exception is set with the
|
||||
-- related Data.
|
||||
|
||||
end Core;
|
||||
|
@ -1,166 +1,348 @@
|
||||
with Ada.Containers.Hashed_Maps; use type Ada.Containers.Count_Type;
|
||||
with Ada.Containers.Hashed_Maps;
|
||||
with Ada.Unchecked_Deallocation;
|
||||
with Atoms;
|
||||
with Names;
|
||||
with Strings; use type Strings.Ptr;
|
||||
with Types; use type Types.Kind_Type;
|
||||
|
||||
package body Environments is
|
||||
|
||||
-- There must be a reference level so that functions may keep
|
||||
-- track of their initial environment, and another one for
|
||||
-- reallocations.
|
||||
use Types;
|
||||
|
||||
package Maps is new Ada.Containers.Hashed_Maps
|
||||
(Key_Type => Strings.Ptr,
|
||||
Element_Type => Types.Mal_Type,
|
||||
Hash => Strings.Hash,
|
||||
Equivalent_Keys => Strings."=",
|
||||
"=" => Types."=");
|
||||
-- The Eval built-in uses the REPL root environment (index 1),
|
||||
-- all others parameters only repeat the top index.
|
||||
|
||||
type Env_Record is limited record
|
||||
Data : Maps.Map;
|
||||
Outer : Env_Access;
|
||||
Refs : Positive;
|
||||
end record;
|
||||
package HM is new Ada.Containers.Hashed_Maps
|
||||
(Key_Type => Symbols.Ptr,
|
||||
Element_Type => Mal.T,
|
||||
Hash => Symbols.Hash,
|
||||
Equivalent_Keys => Symbols."=",
|
||||
"=" => Mal."=");
|
||||
|
||||
procedure Free is new Ada.Unchecked_Deallocation (Object => Env_Record,
|
||||
Name => Env_Access);
|
||||
type Stack_Record
|
||||
(Outer_On_Stack : Boolean := True) is record
|
||||
Data : HM.Map := HM.Empty_Map;
|
||||
Refs : Natural := 1;
|
||||
-- Only references via the Ptr type.
|
||||
-- References from the stack or Alias are not counted here.
|
||||
Alias : Heap_Access := null;
|
||||
-- Used by the closures and heap records to refer to this stack
|
||||
-- record, so that if it moves to the heap we only need to
|
||||
-- update the alias.
|
||||
case Outer_On_Stack is
|
||||
when True =>
|
||||
Outer_Index : Stack_Index := 0;
|
||||
when False =>
|
||||
Outer_Ref : Heap_Access := null;
|
||||
end case;
|
||||
end record
|
||||
with Dynamic_Predicate => 0 < Refs
|
||||
and (Alias = null or else Alias.all.Outer = null)
|
||||
and (if Outer_On_Stack
|
||||
then Outer_Index <= Top
|
||||
else Outer_Ref /= null);
|
||||
|
||||
-- It is forbidden to change the discriminant of an access type,
|
||||
-- so we cannot use a discriminant here.
|
||||
type Heap_Record is limited record
|
||||
Refs : Natural := 1;
|
||||
Data : HM.Map := HM.Empty_Map;
|
||||
Index : Stack_Index;
|
||||
Outer : Heap_Access := null;
|
||||
end record
|
||||
with Dynamic_Predicate =>
|
||||
(if Outer = null
|
||||
then Index in 1 .. Top and Data.Is_Empty
|
||||
else 0 < Refs);
|
||||
-- Either an alias for a stack element or an actual environment.
|
||||
|
||||
-- There could be one single type, but this would enlarge the
|
||||
-- stack without simplifying the code, and prevent some more
|
||||
-- static type checking.
|
||||
|
||||
Stack : array (Stack_Index range 1 .. Stack_Index'Last) of Stack_Record;
|
||||
-- The default value gives a consistent value to Stack (1),
|
||||
-- compatible with the Repl constant.
|
||||
|
||||
procedure Free is new Ada.Unchecked_Deallocation (Heap_Record, Heap_Access);
|
||||
procedure Unreference (Reference : in out Heap_Access);
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
procedure Adjust (Object : in out Ptr) is
|
||||
begin
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs + 1;
|
||||
end Adjust;
|
||||
|
||||
function Alloc return Ptr
|
||||
is (Ada.Finalization.Controlled with
|
||||
Ref => new Env_Record'(Data => Maps.Empty_Map,
|
||||
Outer => null,
|
||||
Refs => 1));
|
||||
|
||||
function Alloc (Outer : in Ptr) return Ptr is
|
||||
begin
|
||||
Outer.Ref.all.Refs := Outer.Ref.all.Refs + 1;
|
||||
return (Ada.Finalization.Controlled with
|
||||
Ref => new Env_Record'(Data => Maps.Empty_Map,
|
||||
Outer => Outer.Ref,
|
||||
Refs => 1));
|
||||
end Alloc;
|
||||
|
||||
procedure Finalize (Object : in out Ptr)
|
||||
is
|
||||
Ref : Env_Access;
|
||||
Refs : Positive;
|
||||
procedure Adjust (Object : in out Closure_Ptr) is
|
||||
begin
|
||||
if Object.Ref /= null then
|
||||
Ref := Object.Ref;
|
||||
Object.Ref := null;
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs + 1;
|
||||
end if;
|
||||
end Adjust;
|
||||
|
||||
function Copy_Pointer (Env : in Ptr) return Ptr is
|
||||
begin
|
||||
Stack (Env.Index).Refs := Stack (Env.Index).Refs + 1;
|
||||
return (Ada.Finalization.Limited_Controlled with Env.Index);
|
||||
end Copy_Pointer;
|
||||
|
||||
-- procedure Dump_Stack (Long : Boolean := False) is
|
||||
-- use Ada.Text_IO;
|
||||
-- use Ada.Text_IO.Unbounded_IO;
|
||||
-- begin
|
||||
-- for I in 1 .. Top loop
|
||||
-- if Long then
|
||||
-- Put ("Level");
|
||||
-- end if;
|
||||
-- Put (I'Img);
|
||||
-- if Long then
|
||||
-- New_Line;
|
||||
-- Put_Line (" refs=" & Stack (I).Refs'Img);
|
||||
-- if Stack (I).Alias = null then
|
||||
-- Put_Line (" no alias");
|
||||
-- else
|
||||
-- Put_Line (" an alias with" & Stack (I).Alias.all.Refs'Img
|
||||
-- & " refs");
|
||||
-- end if;
|
||||
-- end if;
|
||||
-- if Long then
|
||||
-- Put (" outer=");
|
||||
-- else
|
||||
-- Put (" (->");
|
||||
-- end if;
|
||||
-- if Stack (I).Outer_On_Stack then
|
||||
-- Put (Stack (I).Outer_Index'Img);
|
||||
-- elsif Stack (I).Outer_Ref.all.Outer = null then
|
||||
-- if Long then
|
||||
-- Put ("alias for ");
|
||||
-- end if;
|
||||
-- Put (Stack (I).Outer_Ref.all.Index'Img);
|
||||
-- else
|
||||
-- Put (" closure for ex " & Stack (I).Outer_Ref.all.Index'Img);
|
||||
-- end if;
|
||||
-- if Long then
|
||||
-- New_Line;
|
||||
-- else
|
||||
-- Put ("):");
|
||||
-- end if;
|
||||
-- for P in Stack (I).Data.Iterate loop
|
||||
-- if HM.Element (P).Kind /= Kind_Builtin then -- skip built-ins.
|
||||
-- if Long then
|
||||
-- Put (" ");
|
||||
-- else
|
||||
-- Put (' ');
|
||||
-- end if;
|
||||
-- Put (HM.Key (P).To_String);
|
||||
-- Put (':');
|
||||
-- Put (Printer.Pr_Str (HM.Element (P)));
|
||||
-- if Long then
|
||||
-- New_Line;
|
||||
-- end if;
|
||||
-- end if;
|
||||
-- end loop;
|
||||
-- if Long then
|
||||
-- Put (" ... built-ins");
|
||||
-- else
|
||||
-- New_Line;
|
||||
-- end if;
|
||||
-- end loop;
|
||||
-- if Long then
|
||||
-- New_Line;
|
||||
-- end if;
|
||||
-- end Dump_Stack;
|
||||
|
||||
procedure Finalize (Object : in out Closure_Ptr) is
|
||||
begin
|
||||
Unreference (Object.Ref);
|
||||
end Finalize;
|
||||
|
||||
procedure Finalize (Object : in out Ptr) is
|
||||
begin
|
||||
if 0 < Object.Index then
|
||||
if 0 < Stack (Object.Index).Refs then
|
||||
Stack (Object.Index).Refs := Stack (Object.Index).Refs - 1;
|
||||
end if;
|
||||
Object.Index := 0;
|
||||
|
||||
-- If Index = Top and there are no more references.
|
||||
loop
|
||||
Refs := Ref.all.Refs;
|
||||
if 1 < Refs then
|
||||
Ref.all.Refs := Refs - 1;
|
||||
exit;
|
||||
end if;
|
||||
pragma Assert (0 < Top);
|
||||
declare
|
||||
Tmp : Env_Access := Ref;
|
||||
R : Stack_Record renames Stack (Top);
|
||||
begin
|
||||
Ref := Ref.all.Outer;
|
||||
Free (Tmp);
|
||||
exit when 0 < R.Refs;
|
||||
|
||||
if Top = 1 then
|
||||
R.Data.Clear;
|
||||
if R.Alias /= null then
|
||||
pragma Assert (R.Alias.all.Outer = null);
|
||||
pragma Assert (R.Alias.all.Refs = 0);
|
||||
Free (R.Alias);
|
||||
end if;
|
||||
exit;
|
||||
elsif R.Alias = null then
|
||||
R.Data.Clear;
|
||||
if not R.Outer_On_Stack then
|
||||
Unreference (R.Outer_Ref);
|
||||
end if;
|
||||
elsif R.Alias.all.Refs = 0 then
|
||||
pragma Assert (R.Alias.all.Outer = null);
|
||||
Free (R.Alias);
|
||||
R.Data.Clear;
|
||||
if not R.Outer_On_Stack then
|
||||
Unreference (R.Outer_Ref);
|
||||
end if;
|
||||
else
|
||||
-- Detach this environment from the stack.
|
||||
|
||||
-- The reference count is already correct.
|
||||
|
||||
-- Copy the hashmap contents without reallocation..
|
||||
R.Alias.all.Data.Move (R.Data);
|
||||
|
||||
-- The Index will not be used anymore.
|
||||
|
||||
-- We need the parent to have an alias, in case it
|
||||
-- must be detached later.
|
||||
if R.Outer_On_Stack then
|
||||
declare
|
||||
O : Stack_Record renames Stack (R.Outer_Index);
|
||||
begin
|
||||
if O.Alias = null then
|
||||
O.Alias := new Heap_Record'(Index => R.Outer_Index,
|
||||
others => <>);
|
||||
else
|
||||
O.Alias.all.Refs := O.Alias.all.Refs + 1;
|
||||
end if;
|
||||
R.Alias.all.Outer := O.Alias;
|
||||
end;
|
||||
else
|
||||
R.Alias.all.Outer := R.Outer_Ref;
|
||||
end if;
|
||||
R.Alias := null;
|
||||
end if;
|
||||
end;
|
||||
exit when Ref = null;
|
||||
Top := Top - 1;
|
||||
end loop;
|
||||
end if;
|
||||
end Finalize;
|
||||
|
||||
function Get (Container : in Ptr;
|
||||
Key : in Strings.Ptr) return Types.Mal_Type
|
||||
is
|
||||
Ref : Env_Access := Container.Ref;
|
||||
Position : Maps.Cursor;
|
||||
function Get (Env : in Ptr;
|
||||
Key : in Symbols.Ptr)
|
||||
return Mal.T is
|
||||
Index : Stack_Index := Env.Index;
|
||||
Ref : Heap_Access;
|
||||
Definition : HM.Cursor;
|
||||
begin
|
||||
loop
|
||||
Position := Ref.all.Data.Find (Key);
|
||||
if Maps.Has_Element (Position) then
|
||||
return Ref.all.Data (Position);
|
||||
end if;
|
||||
Ref := Ref.all.Outer;
|
||||
exit when Ref = null;
|
||||
end loop;
|
||||
raise Unknown_Key with "'" & Key.Deref & "' not found";
|
||||
Main_Loop : loop
|
||||
Index_Loop : loop
|
||||
Definition := Stack (Index).Data.Find (Key);
|
||||
if HM.Has_Element (Definition) then
|
||||
return HM.Element (Definition);
|
||||
end if;
|
||||
exit Index_Loop when not Stack (Index).Outer_On_Stack;
|
||||
Index := Stack (Index).Outer_Index;
|
||||
exit Main_Loop when Index = 0;
|
||||
end loop Index_Loop;
|
||||
Ref := Stack (Index).Outer_Ref;
|
||||
Ref_Loop : loop
|
||||
Definition := Ref.all.Data.Find (Key);
|
||||
if HM.Has_Element (Definition) then
|
||||
return HM.Element (Definition);
|
||||
end if;
|
||||
exit Ref_Loop when Ref.all.Outer = null;
|
||||
Ref := Ref.all.Outer;
|
||||
end loop Ref_Loop;
|
||||
Index := Ref.all.Index;
|
||||
end loop Main_Loop;
|
||||
raise Unknown_Key with "'" & Key.To_String & "' not found";
|
||||
end Get;
|
||||
|
||||
procedure Increase_Capacity (Container : in Ptr;
|
||||
Capacity : in Natural)
|
||||
is
|
||||
New_Capacity : constant Ada.Containers.Count_Type
|
||||
:= Container.Ref.all.Data.Length
|
||||
+ Ada.Containers.Count_Type (Capacity);
|
||||
function New_Closure (Env : in Ptr'Class) return Closure_Ptr is
|
||||
Alias : Heap_Access renames Stack (Env.Index).Alias;
|
||||
begin
|
||||
if Container.Ref.all.Data.Capacity < New_Capacity then
|
||||
Container.Ref.all.Data.Reserve_Capacity (New_Capacity);
|
||||
if Alias = null then
|
||||
Alias := new Heap_Record'(Index => Env.Index, others => <>);
|
||||
else
|
||||
Alias.all.Refs := Alias.all.Refs + 1;
|
||||
end if;
|
||||
end Increase_Capacity;
|
||||
return (Ada.Finalization.Controlled with Alias);
|
||||
end New_Closure;
|
||||
|
||||
procedure Replace_With_Subenv (Item : in out Ptr) is
|
||||
procedure Replace_With_Sub (Env : in out Ptr) is
|
||||
R : Stack_Record renames Stack (Env.Index);
|
||||
begin
|
||||
if 1 < Item.Ref.all.Refs then
|
||||
Item.Ref := new Env_Record'(Data => Maps.Empty_Map,
|
||||
Outer => Item.Ref,
|
||||
Refs => 1);
|
||||
if Env.Index < Top or 1 < R.Refs
|
||||
or (R.Alias /= null and then 0 < R.Alias.all.Refs)
|
||||
then
|
||||
R.Refs := R.Refs - 1;
|
||||
Top := Top + 1;
|
||||
pragma Assert (Stack (Top).Data.Is_Empty);
|
||||
pragma Assert (Stack (Top).Alias = null);
|
||||
Stack (Top) := (Outer_Index => Env.Index,
|
||||
others => <>);
|
||||
Env.Index := Top;
|
||||
end if;
|
||||
end Replace_With_Subenv;
|
||||
-- Else reuse the top stack record, including its map and its
|
||||
-- unreferenced alias if any.
|
||||
end Replace_With_Sub;
|
||||
|
||||
procedure Set (Container : in Ptr;
|
||||
Key : in Strings.Ptr;
|
||||
New_Item : in Types.Mal_Type) is
|
||||
procedure Replace_With_Sub (Env : in out Ptr;
|
||||
Outer : in Closure_Ptr'Class) is
|
||||
begin
|
||||
Container.Ref.all.Data.Include (Key, New_Item);
|
||||
Finalize (Env);
|
||||
Outer.Ref.all.Refs := Outer.Ref.all.Refs + 1;
|
||||
Top := Top + 1;
|
||||
pragma Assert (Stack (Top).Data.Is_Empty);
|
||||
pragma Assert (Stack (Top).Alias = null);
|
||||
Stack (Top) := (Outer_On_Stack => False,
|
||||
Outer_Ref => Outer.Ref,
|
||||
others => <>);
|
||||
Env.Index := Top;
|
||||
end Replace_With_Sub;
|
||||
|
||||
procedure Set (Env : in Ptr;
|
||||
Key : in Symbols.Ptr;
|
||||
New_Element : in Mal.T) is
|
||||
begin
|
||||
Stack (Env.Index).Data.Include (Key, New_Element);
|
||||
end Set;
|
||||
|
||||
procedure Set_Binds (Container : in Ptr;
|
||||
Formals : in Lists.Ptr;
|
||||
Actuals : in Types.Mal_Type_Array)
|
||||
is
|
||||
-- The assertions should be a precondition, but cannot be
|
||||
-- expressed with a "limited with" view on Types.
|
||||
function Sub (Outer : in Closure_Ptr'Class) return Ptr is
|
||||
begin
|
||||
if Formals.Length <= 1
|
||||
or else Formals.Element (Formals.Length - 1).S /= Names.Ampersand
|
||||
then
|
||||
pragma Assert (Formals.Length = Actuals'Length);
|
||||
pragma Assert (for all I in 1 .. Formals.Length =>
|
||||
Formals.Element (I).Kind = Types.Kind_Symbol
|
||||
and then Formals.Element (I).S /= Names.Ampersand);
|
||||
Increase_Capacity (Container, Formals.Length);
|
||||
for I in 1 .. Formals.Length loop
|
||||
Container.Ref.all.Data.Include (Formals.Element (I).S,
|
||||
Actuals (Actuals'First + I - 1));
|
||||
end loop;
|
||||
else
|
||||
Outer.Ref.all.Refs := Outer.Ref.all.Refs + 1;
|
||||
Top := Top + 1;
|
||||
pragma Assert (Stack (Top).Data.Is_Empty);
|
||||
pragma Assert (Stack (Top).Alias = null);
|
||||
Stack (Top) := (Outer_On_Stack => False,
|
||||
Outer_Ref => Outer.Ref,
|
||||
others => <>);
|
||||
return (Ada.Finalization.Limited_Controlled with Top);
|
||||
end Sub;
|
||||
|
||||
function Sub (Outer : in Ptr) return Ptr is
|
||||
R : Stack_Record renames Stack (Outer.Index);
|
||||
begin
|
||||
R.Refs := R.Refs + 1;
|
||||
Top := Top + 1;
|
||||
pragma Assert (Stack (Top).Data.Is_Empty);
|
||||
pragma Assert (Stack (Top).Alias = null);
|
||||
Stack (Top) := (Outer_Index => Outer.Index,
|
||||
others => <>);
|
||||
return (Ada.Finalization.Limited_Controlled with Top);
|
||||
end Sub;
|
||||
|
||||
procedure Unreference (Reference : in out Heap_Access) is
|
||||
Ref : Heap_Access := Reference;
|
||||
begin
|
||||
Reference := null;
|
||||
loop
|
||||
exit when Ref = null;
|
||||
exit when Ref.all.Refs = 0;
|
||||
Ref.all.Refs := Ref.all.Refs - 1;
|
||||
exit when 0 < Ref.all.Refs;
|
||||
exit when Ref.all.Outer = null; -- An alias. Do not free it
|
||||
-- now, it may be useful for another closure.
|
||||
declare
|
||||
Len : constant Natural := Formals.Length - 2;
|
||||
Tmp : Heap_Access := Ref;
|
||||
begin
|
||||
pragma Assert (Len <= Actuals'Length);
|
||||
pragma Assert (for all I in 1 .. Len =>
|
||||
Formals.Element (I).Kind = Types.Kind_Symbol
|
||||
and then Formals.Element (I).S /= Names.Ampersand);
|
||||
pragma Assert (Formals.Element (Len + 1).Kind = Types.Kind_Symbol);
|
||||
pragma Assert (Formals.Element (Len + 1).S = Names.Ampersand);
|
||||
pragma Assert (Formals.Element (Len + 2).Kind = Types.Kind_Symbol);
|
||||
pragma Assert (Formals.Element (Len + 2).S /= Names.Ampersand);
|
||||
Increase_Capacity (Container, Len + 1);
|
||||
for I in 1 .. Len loop
|
||||
Container.Ref.all.Data.Include
|
||||
(Formals.Element (I).S, Actuals (Actuals'First + I - 1));
|
||||
end loop;
|
||||
Container.Ref.all.Data.Include
|
||||
(Formals.Element (Formals.Length).S,
|
||||
(Types.Kind_List, Atoms.No_Element,
|
||||
Lists.Alloc (Actuals (Actuals'First + Len .. Actuals'Last))));
|
||||
Ref := Ref.all.Outer;
|
||||
Free (Tmp);
|
||||
pragma Unreferenced (Tmp);
|
||||
end;
|
||||
end if;
|
||||
end Set_Binds;
|
||||
end loop;
|
||||
end Unreference;
|
||||
|
||||
end Environments;
|
||||
|
@ -1,54 +1,115 @@
|
||||
private with Ada.Finalization;
|
||||
with Lists;
|
||||
with Strings;
|
||||
limited with Types;
|
||||
|
||||
package Environments is
|
||||
with Types.Mal;
|
||||
with Types.Symbols;
|
||||
|
||||
type Ptr is tagged private;
|
||||
-- Any variable must be assigned immediately with one of the two
|
||||
-- following functions.
|
||||
function Alloc return Ptr
|
||||
with Inline;
|
||||
function Alloc (Outer : in Ptr) return Ptr
|
||||
with Inline;
|
||||
-- A hidden invariant ensures this when assertions are enabled.
|
||||
package Environments with Elaborate_Body is
|
||||
|
||||
procedure Increase_Capacity (Container : in Ptr;
|
||||
Capacity : in Natural)
|
||||
-- This implementation relies on the fact that the caller only
|
||||
-- ever references environments in its execution stack.
|
||||
|
||||
-- When a function closure references an environment that the
|
||||
-- execution leaves behind, a dynamically allocated block is used
|
||||
-- instead.
|
||||
|
||||
-- The eval built-in requires REPL (see the implementation of
|
||||
-- load-file), so we cannot assume that the caller only sees the
|
||||
-- current environment.
|
||||
|
||||
type Ptr (<>) is tagged limited private;
|
||||
-- This type is controlled in order count the references to a
|
||||
-- given environment, even during exception propagation.
|
||||
-- Since Ptr is limited with a hidden discriminant, any variable
|
||||
-- must immediately be assigned with one of
|
||||
-- * Repl (in which case a renaming is probably better),
|
||||
-- * Copy_Pointer,
|
||||
-- * Sub (either from a Ptr or from a Closure_Ptr).
|
||||
-- Usual assignment with reference counting is not provided
|
||||
-- because we want to enforce the use of the more efficient
|
||||
-- Replace_With_Sub.
|
||||
|
||||
Repl : constant Ptr;
|
||||
-- The top environment.
|
||||
|
||||
function Copy_Pointer (Env : in Ptr) return Ptr with Inline;
|
||||
|
||||
function Sub (Outer : in Ptr) return Ptr with Inline;
|
||||
|
||||
procedure Replace_With_Sub (Env : in out Ptr) with Inline;
|
||||
-- Like Env := Sub (Outer => Env); except that Env is finalized
|
||||
-- *before* the assignement, so its memory may be reused by the
|
||||
-- new environment.
|
||||
|
||||
procedure Set (Env : in Ptr;
|
||||
Key : in Types.Symbols.Ptr;
|
||||
New_Element : in Types.Mal.T)
|
||||
with Inline;
|
||||
|
||||
procedure Replace_With_Subenv (Item : in out Ptr)
|
||||
with Inline;
|
||||
-- Equivalent to Item := Alloc (Outer => Item, Capacity), but
|
||||
-- faster when Item was the last reference to its environment, as
|
||||
-- the storage and maps are then reused.
|
||||
|
||||
procedure Set (Container : in Ptr;
|
||||
Key : in Strings.Ptr;
|
||||
New_Item : in Types.Mal_Type)
|
||||
with Inline;
|
||||
|
||||
procedure Set_Binds (Container : in Ptr;
|
||||
Formals : in Lists.Ptr;
|
||||
Actuals : in Types.Mal_Type_Array);
|
||||
|
||||
function Get (Container : in Ptr;
|
||||
Key : in Strings.Ptr) return Types.Mal_Type;
|
||||
function Get (Env : in Ptr;
|
||||
Key : in Types.Symbols.Ptr)
|
||||
return Types.Mal.T;
|
||||
Unknown_Key : exception;
|
||||
|
||||
-- procedure Dump;
|
||||
-- Function closures.
|
||||
|
||||
type Closure_Ptr is tagged private;
|
||||
Null_Closure : constant Closure_Ptr;
|
||||
|
||||
function Sub (Outer : in Closure_Ptr'Class) return Ptr;
|
||||
|
||||
procedure Replace_With_Sub (Env : in out Ptr;
|
||||
Outer : in Closure_Ptr'Class);
|
||||
-- Like Env := Sub (Outer => Outer); except that Env is finalized
|
||||
-- *before* the assignement, so its memory can be reused by the
|
||||
-- new environment. This is important for tail call optimization.
|
||||
|
||||
function New_Closure (Env : in Ptr'Class) return Closure_Ptr;
|
||||
-- The class-wide argument does not make much sense, but avoids
|
||||
-- the compiler wondering on which type is should dispatch.
|
||||
|
||||
private
|
||||
|
||||
type Env_Record;
|
||||
type Env_Access is access Env_Record;
|
||||
type Ptr is new Ada.Finalization.Controlled with record
|
||||
Ref : Env_Access := null;
|
||||
-- There must be a reference level so that functions may keep
|
||||
-- track of their initial environment, and another one for
|
||||
-- reallocations. The second one is delegated to a predefined Ada
|
||||
-- container.
|
||||
|
||||
-- MAL maps may be tempting, but we do not want to copy the whole
|
||||
-- map for each addition or removal.
|
||||
|
||||
-- Some tests seem to show that a hashmap is three times faster
|
||||
-- than a vector with (key, value) couples.
|
||||
|
||||
-- We allow the null value so that the empty environment in a
|
||||
-- macro does not trigger an allocation.
|
||||
|
||||
-- 300 for normal tests
|
||||
-- 7_500 for make ada2 && make MAL_IMPL=ada2 test^mal
|
||||
-- 150_000 for make ada2 && make perf^ada2
|
||||
type Stack_Index is range 0 .. 150_000;
|
||||
|
||||
-- See README for the implementation of reference counting.
|
||||
|
||||
type Ptr is new Ada.Finalization.Limited_Controlled with record
|
||||
Index : Stack_Index := 0;
|
||||
end record
|
||||
with Invariant => Ptr.Ref /= null;
|
||||
overriding procedure Adjust (Object : in out Ptr) with Inline;
|
||||
overriding procedure Finalize (Object : in out Ptr);
|
||||
-- Predefined equality is fine.
|
||||
with Invariant => Index in 1 .. Top;
|
||||
overriding procedure Finalize (Object : in out Ptr) with Inline;
|
||||
pragma Finalize_Storage_Only (Ptr);
|
||||
|
||||
Top : Stack_Index := 1;
|
||||
Repl : constant Ptr := (Ada.Finalization.Limited_Controlled with 1);
|
||||
|
||||
type Heap_Record;
|
||||
type Heap_Access is access Heap_Record;
|
||||
type Closure_Ptr is new Ada.Finalization.Controlled with record
|
||||
Ref : Heap_Access := null;
|
||||
end record;
|
||||
overriding procedure Adjust (Object : in out Closure_Ptr) with Inline;
|
||||
overriding procedure Finalize (Object : in out Closure_Ptr) with Inline;
|
||||
pragma Finalize_Storage_Only (Closure_Ptr);
|
||||
|
||||
Null_Closure : constant Closure_Ptr
|
||||
:= (Ada.Finalization.Controlled with null);
|
||||
|
||||
end Environments;
|
||||
|
@ -1,95 +0,0 @@
|
||||
with Ada.Unchecked_Deallocation;
|
||||
with Atoms;
|
||||
with Types;
|
||||
|
||||
package body Lists is
|
||||
|
||||
type List_Record (Last : Positive) is limited record
|
||||
Data : Types.Mal_Type_Array (1 .. Last);
|
||||
Refs : Positive;
|
||||
end record;
|
||||
-- The invariant for Ptr is:
|
||||
-- Ptr.Ref = null or else Ptr.First <= Ptr.Ref.all.Last
|
||||
-- but we cannot express this in the specification because the limited
|
||||
-- view on Types forbids to define List_Record there.
|
||||
|
||||
procedure Free is new Ada.Unchecked_Deallocation (Object => List_Record,
|
||||
Name => List_Access);
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function "=" (Left, Right : in Ptr) return Boolean is
|
||||
(if Left.Ref = null
|
||||
then Right.Ref = null
|
||||
else
|
||||
-- As strange as it may seem, this assertion fails when
|
||||
-- running "(= [(list)] (list []))".
|
||||
-- pragma Assert
|
||||
-- ((Left.Ref.all.Data (1) = Right.Ref.all.Data (1))
|
||||
-- =
|
||||
-- (Left.Ref.all.Data (1 .. 1) = Right.Ref.all.Data (1 .. 1)));
|
||||
-- This may be a compiler bug.
|
||||
Right.Ref /= null
|
||||
and then Left.Ref.all.Last = Right.Ref.all.Last
|
||||
and then (for all I in 1 .. Left.Ref.all.Last =>
|
||||
Types."=" (Left.Ref.all.Data (I),
|
||||
Right.Ref.all.Data (I))));
|
||||
|
||||
procedure Adjust (Object : in out Ptr) is
|
||||
begin
|
||||
if Object.Ref /= null then
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs + 1;
|
||||
end if;
|
||||
end Adjust;
|
||||
|
||||
function Element (Container : in Ptr;
|
||||
Index : in Positive) return Types.Mal_Type is
|
||||
(Container.Ref.all.Data (Index));
|
||||
|
||||
procedure Finalize (Object : in out Ptr)
|
||||
is
|
||||
Refs : Positive;
|
||||
begin
|
||||
-- Ensure that we can be called twice in a row (7.6.1(24)).
|
||||
if Object.Ref /= null then
|
||||
Refs := Object.Ref.all.Refs;
|
||||
if 1 < Refs then
|
||||
Object.Ref.all.Refs := Refs - 1;
|
||||
Object.Ref := null;
|
||||
else
|
||||
-- pragma Assert (Ptr (Object.Ref.all.Id) = Object.Ref);
|
||||
-- Ptr (Object.Ref.all.Id) := null;
|
||||
Free (Object.Ref);
|
||||
end if;
|
||||
end if;
|
||||
end Finalize;
|
||||
|
||||
function Length (Source : in Ptr) return Natural is
|
||||
(if Source.Ref = null then 0 else Source.Ref.all.Last);
|
||||
|
||||
function Alloc (Source : in Types.Mal_Type_Array) return Ptr is
|
||||
(if Source'Length = 0
|
||||
then Empty_List
|
||||
else (Ada.Finalization.Controlled with
|
||||
Ref => new List_Record'(Data => Source,
|
||||
Last => Source'Length,
|
||||
Refs => 1)));
|
||||
|
||||
function Alloc (Length : in Natural) return Ptr is
|
||||
(if Length = 0
|
||||
then Empty_List
|
||||
else (Ada.Finalization.Controlled with
|
||||
Ref => new List_Record'
|
||||
(Data => (1 .. Length => (Types.Kind_Nil, Atoms.No_Element)),
|
||||
Last => Length,
|
||||
Refs => 1)));
|
||||
|
||||
procedure Replace_Element (Source : in Ptr;
|
||||
Index : in Positive;
|
||||
New_Value : in Types.Mal_Type) is
|
||||
begin
|
||||
pragma Assert (Source.Ref.all.Refs = 1);
|
||||
Source.Ref.all.Data (Index) := New_Value;
|
||||
end Replace_Element;
|
||||
|
||||
end Lists;
|
@ -1,47 +0,0 @@
|
||||
private with Ada.Finalization;
|
||||
limited with Types;
|
||||
|
||||
package Lists is
|
||||
|
||||
-- A pointer to an array of Mal_Type elements. It differs from
|
||||
-- Ada.Containers.Vectors because assignment give another pointer
|
||||
-- to the same storage and does not copy contents.
|
||||
|
||||
type Ptr is tagged private;
|
||||
Empty_List : constant Ptr; -- The default value.
|
||||
|
||||
function Length (Source : in Ptr) return Natural
|
||||
with Inline;
|
||||
|
||||
function Element (Container : in Ptr;
|
||||
Index : in Positive) return Types.Mal_Type
|
||||
with Inline, Pre => Index <= Container.Length;
|
||||
|
||||
function Alloc (Length : in Natural) return Ptr
|
||||
with Inline;
|
||||
-- All elements are Nil, the default value for Mal_Type.
|
||||
|
||||
function Alloc (Source : in Types.Mal_Type_Array) return Ptr
|
||||
with Inline;
|
||||
|
||||
procedure Replace_Element (Source : in Ptr;
|
||||
Index : in Positive;
|
||||
New_Value : in Types.Mal_Type)
|
||||
with Inline, Pre => Index <= Source.Length;
|
||||
-- An assertion checks that Source is the only reference to its
|
||||
-- storage.
|
||||
|
||||
private
|
||||
|
||||
type List_Record;
|
||||
type List_Access is access List_Record;
|
||||
type Ptr is new Ada.Finalization.Controlled with record
|
||||
Ref : List_Access := null;
|
||||
end record;
|
||||
overriding procedure Adjust (Object : in out Ptr) with Inline;
|
||||
overriding procedure Finalize (Object : in out Ptr) with Inline;
|
||||
overriding function "=" (Left, Right : in Ptr) return Boolean;
|
||||
|
||||
Empty_List : constant Ptr := (Ada.Finalization.Controlled with Ref => null);
|
||||
|
||||
end Lists;
|
160
ada2/maps.adb
160
ada2/maps.adb
@ -1,160 +0,0 @@
|
||||
with Ada.Containers.Hashed_Maps;
|
||||
with Ada.Unchecked_Deallocation;
|
||||
with Strings;
|
||||
with Types;
|
||||
|
||||
package body Maps is
|
||||
|
||||
function Hash (Item : in Types.Mal_Type) return Ada.Containers.Hash_Type
|
||||
with Inline, Pre => Item.Kind in Types.Kind_String | Types.Kind_Keyword;
|
||||
|
||||
package Hashed_Maps is new Ada.Containers.Hashed_Maps
|
||||
(Key_Type => Types.Mal_Type,
|
||||
Element_Type => Types.Mal_Type,
|
||||
Hash => Hash,
|
||||
Equivalent_Keys => Types."=",
|
||||
"=" => Types."=");
|
||||
|
||||
type Map_Record is limited record
|
||||
Data : Hashed_Maps.Map;
|
||||
Refs : Positive;
|
||||
end record;
|
||||
|
||||
procedure Free is new Ada.Unchecked_Deallocation (Object => Map_Record,
|
||||
Name => Map_Access);
|
||||
|
||||
use type Ada.Containers.Count_Type;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function "=" (Left, Right : in Ptr) return Boolean is
|
||||
(Hashed_Maps."=" (Left.Ref.all.Data, Right.Ref.all.Data));
|
||||
|
||||
procedure Adjust (Object : in out Ptr) is
|
||||
begin
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs + 1;
|
||||
end Adjust;
|
||||
|
||||
function Assoc (Container : in Ptr;
|
||||
Pairs : in Types.Mal_Type_Array) return Ptr
|
||||
is
|
||||
pragma Assert (Pairs'Length mod 2 = 0);
|
||||
Pair_Count : constant Ada.Containers.Count_Type
|
||||
:= Ada.Containers.Count_Type (Pairs'Length) / 2;
|
||||
Result : Ptr;
|
||||
begin
|
||||
Result.Ref.all.Data.Reserve_Capacity (Pair_Count
|
||||
+ Container.Ref.all.Data.Length);
|
||||
Result.Ref.all.Data.Assign (Container.Ref.all.Data);
|
||||
for I in 0 .. Pairs'Length / 2 - 1 loop
|
||||
pragma Assert (Pairs (Pairs'First + 2 * I).Kind in Types.Kind_String
|
||||
| Types.Kind_Keyword);
|
||||
Result.Ref.all.Data.Include (Pairs (Pairs'First + 2 * I),
|
||||
Pairs (Pairs'First + 2 * I + 1));
|
||||
end loop;
|
||||
return Result;
|
||||
end Assoc;
|
||||
|
||||
function Contains (Container : in Ptr;
|
||||
Key : in Types.Mal_Type) return Boolean is
|
||||
(Container.Ref.all.Data.Contains (Key));
|
||||
|
||||
function Dissoc (Source : in Ptr;
|
||||
Keys : in Types.Mal_Type_Array) return Ptr
|
||||
is
|
||||
Result : Ptr;
|
||||
begin
|
||||
Result.Ref.all.Data.Assign (Source.Ref.all.Data);
|
||||
for I in Keys'Range loop
|
||||
pragma Assert (Keys (I).Kind in Types.Kind_String
|
||||
| Types.Kind_Keyword);
|
||||
Result.Ref.all.Data.Exclude (Keys (I));
|
||||
end loop;
|
||||
return Result;
|
||||
end Dissoc;
|
||||
|
||||
procedure Finalize (Object : in out Ptr)
|
||||
is
|
||||
Refs : Positive;
|
||||
begin
|
||||
-- Finalize may be called twice.
|
||||
if Object.Ref /= null then
|
||||
Refs := Object.Ref.all.Refs;
|
||||
if 1 < Refs then
|
||||
Object.Ref.all.Refs := Refs - 1;
|
||||
Object.Ref := null;
|
||||
else
|
||||
Free (Object.Ref);
|
||||
end if;
|
||||
end if;
|
||||
end Finalize;
|
||||
|
||||
procedure Iterate
|
||||
(Container : in Ptr;
|
||||
Process : not null access procedure (Key : in Types.Mal_Type;
|
||||
Element : in Types.Mal_Type)) is
|
||||
begin
|
||||
for Position in Container.Ref.all.Data.Iterate loop
|
||||
Process.all (Hashed_Maps.Key (Position),
|
||||
Hashed_Maps.Element (Position));
|
||||
end loop;
|
||||
end Iterate;
|
||||
|
||||
function Get (Container : in Ptr;
|
||||
Key : in Types.Mal_Type) return Types.Mal_Type
|
||||
is
|
||||
Position : Hashed_Maps.Cursor;
|
||||
begin
|
||||
Position := Container.Ref.all.Data.Find (Key);
|
||||
if Hashed_Maps.Has_Element (Position) then
|
||||
return Hashed_Maps.Element (Position);
|
||||
end if;
|
||||
raise Unknown_Key with "'" & Key.S.Deref & "' not found";
|
||||
end Get;
|
||||
|
||||
function Hash (Item : in Types.Mal_Type) return Ada.Containers.Hash_Type is
|
||||
(Item.S.Hash);
|
||||
|
||||
function Hash_Map (Pairs : in Types.Mal_Type_Array) return Ptr
|
||||
is
|
||||
pragma Assert (Pairs'Length mod 2 = 0);
|
||||
Pair_Count : constant Ada.Containers.Count_Type
|
||||
:= Ada.Containers.Count_Type (Pairs'Length) / 2;
|
||||
Result : Ptr;
|
||||
begin
|
||||
Result.Ref.all.Data.Reserve_Capacity (Pair_Count);
|
||||
for I in 0 .. Pairs'Length / 2 - 1 loop
|
||||
pragma Assert (Pairs (Pairs'First + 2 * I).Kind in Types.Kind_String
|
||||
| Types.Kind_Keyword);
|
||||
Result.Ref.all.Data.Include (Pairs (Pairs'First + 2 * I),
|
||||
Pairs (Pairs'First + 2 * I + 1));
|
||||
end loop;
|
||||
return Result;
|
||||
end Hash_Map;
|
||||
|
||||
procedure Initialize (Object : in out Ptr) is
|
||||
begin
|
||||
Object.Ref := new Map_Record'(Data => Hashed_Maps.Empty_Map,
|
||||
Refs => 1);
|
||||
end Initialize;
|
||||
|
||||
function Length (Container : in Ptr) return Natural
|
||||
is (Natural (Container.Ref.all.Data.Length));
|
||||
|
||||
function Map (Container : in Ptr;
|
||||
F : not null access function (X : in Types.Mal_Type)
|
||||
return Types.Mal_Type)
|
||||
return Ptr
|
||||
is
|
||||
Result : Ptr;
|
||||
begin
|
||||
Result.Ref.all.Data.Assign (Container.Ref.all.Data);
|
||||
-- Ensure the invariants before calling F, in case it raises exceptions.
|
||||
for Position in Result.Ref.all.Data.Iterate loop
|
||||
Result.Ref.all.Data.Replace_Element
|
||||
(Position, F.all (Hashed_Maps.Element (Position)));
|
||||
end loop;
|
||||
return Result;
|
||||
end Map;
|
||||
|
||||
end Maps;
|
@ -1,59 +0,0 @@
|
||||
private with Ada.Finalization;
|
||||
with Lists;
|
||||
limited with Types;
|
||||
|
||||
package Maps is
|
||||
|
||||
-- A pointer to an Ada.Containers.Hashed_Maps.Map of
|
||||
-- Types.Mal_Type. Keys must be Strings or Keywords. We can
|
||||
-- probably not state this with a limited with, so this will
|
||||
-- become an assertion.
|
||||
|
||||
type Ptr is tagged private;
|
||||
-- The default value is empty.
|
||||
|
||||
function Length (Container : in Ptr) return Natural
|
||||
with Inline;
|
||||
|
||||
function Hash_Map (Pairs : in Types.Mal_Type_Array) return Ptr;
|
||||
|
||||
function Assoc (Container : in Ptr;
|
||||
Pairs : in Types.Mal_Type_Array) return Ptr;
|
||||
|
||||
function Dissoc (Source : in Ptr;
|
||||
Keys : in Types.Mal_Type_Array) return Ptr;
|
||||
|
||||
function Map (Container : in Ptr;
|
||||
F : not null access function (X : in Types.Mal_Type)
|
||||
return Types.Mal_Type)
|
||||
return Ptr;
|
||||
|
||||
procedure Iterate
|
||||
(Container : in Ptr;
|
||||
Process : not null access procedure (Key : in Types.Mal_Type;
|
||||
Element : in Types.Mal_Type))
|
||||
with Inline;
|
||||
|
||||
function Contains (Container : in Ptr;
|
||||
Key : in Types.Mal_Type) return Boolean
|
||||
with Inline;
|
||||
|
||||
function Get (Container : in Ptr;
|
||||
Key : in Types.Mal_Type) return Types.Mal_Type
|
||||
with Inline;
|
||||
Unknown_Key : exception;
|
||||
|
||||
private
|
||||
|
||||
type Map_Record;
|
||||
type Map_Access is access Map_Record;
|
||||
type Ptr is new Ada.Finalization.Controlled with record
|
||||
Ref : Map_Access := null;
|
||||
end record
|
||||
with Invariant => Ptr.Ref /= null;
|
||||
overriding procedure Initialize (Object : in out Ptr) with Inline;
|
||||
overriding procedure Adjust (Object : in out Ptr) with Inline;
|
||||
overriding procedure Finalize (Object : in out Ptr) with Inline;
|
||||
overriding function "=" (Left, Right : in Ptr) return Boolean with Inline;
|
||||
|
||||
end Maps;
|
@ -1,87 +0,0 @@
|
||||
with Strings; use Strings;
|
||||
|
||||
package Names is
|
||||
|
||||
-- Symbols known at compile time are allocated at program
|
||||
-- start, in order to avoid repeated allocations and
|
||||
-- deallocations during each Read and /Eval/Print cycle. The
|
||||
-- reference is kept so each usage does not trigger a search in
|
||||
-- the global hash map.
|
||||
|
||||
Ada2 : constant Ptr := Alloc ("ada2");
|
||||
Ampersand : constant Ptr := Alloc ("&");
|
||||
Apply : constant Ptr := Alloc ("apply");
|
||||
Argv : constant Ptr := Alloc ("*ARGV*");
|
||||
Assoc : constant Ptr := Alloc ("assoc");
|
||||
Asterisk : constant Ptr := Alloc ("*");
|
||||
Atom : constant Ptr := Alloc ("atom");
|
||||
Catch : constant Ptr := Alloc ("catch*");
|
||||
Concat : constant Ptr := Alloc ("concat");
|
||||
Conj : constant Ptr := Alloc ("conj");
|
||||
Cons : constant Ptr := Alloc ("cons");
|
||||
Contains : constant Ptr := Alloc ("contains?");
|
||||
Count : constant Ptr := Alloc ("count");
|
||||
Def : constant Ptr := Alloc ("def!");
|
||||
Defmacro : constant Ptr := Alloc ("defmacro!");
|
||||
Deref : constant Ptr := Alloc ("deref");
|
||||
Dissoc : constant Ptr := Alloc ("dissoc");
|
||||
Equals : constant Ptr := Alloc ("=");
|
||||
Eval : constant Ptr := Alloc ("eval");
|
||||
First : constant Ptr := Alloc ("first");
|
||||
Fn : constant Ptr := Alloc ("fn*");
|
||||
Get : constant Ptr := Alloc ("get");
|
||||
Greater_Equal : constant Ptr := Alloc (">=");
|
||||
Greater_Than : constant Ptr := Alloc (">");
|
||||
Hash_Map : constant Ptr := Alloc ("hash-map");
|
||||
Host_Language : constant Ptr := Alloc ("*host-language*");
|
||||
Is_Atom : constant Ptr := Alloc ("atom?");
|
||||
Is_Empty : constant Ptr := Alloc ("empty?");
|
||||
Is_False : constant Ptr := Alloc ("false?");
|
||||
Is_Keyword : constant Ptr := Alloc ("keyword?");
|
||||
Is_List : constant Ptr := Alloc ("list?");
|
||||
Is_Map : constant Ptr := Alloc ("map?");
|
||||
Is_Nil : constant Ptr := Alloc ("nil?");
|
||||
Is_Sequential : constant Ptr := Alloc ("sequential?");
|
||||
Is_String : constant Ptr := Alloc ("string?");
|
||||
Is_Symbol : constant Ptr := Alloc ("symbol?");
|
||||
Is_True : constant Ptr := Alloc ("true?");
|
||||
Is_Vector : constant Ptr := Alloc ("vector?");
|
||||
Keys : constant Ptr := Alloc ("keys");
|
||||
Keyword : constant Ptr := Alloc ("keyword");
|
||||
Less_Equal : constant Ptr := Alloc ("<=");
|
||||
Less_Than : constant Ptr := Alloc ("<");
|
||||
Let : constant Ptr := Alloc ("let*");
|
||||
List : constant Ptr := Alloc ("list");
|
||||
Macroexpand : constant Ptr := Alloc ("macroexpand");
|
||||
Mal_Do : constant Ptr := Alloc ("do");
|
||||
Mal_If : constant Ptr := Alloc ("if");
|
||||
Map : constant Ptr := Alloc ("map");
|
||||
Meta : constant Ptr := Alloc ("meta");
|
||||
Minus : constant Ptr := Alloc ("-");
|
||||
Nth : constant Ptr := Alloc ("nth");
|
||||
Plus : constant Ptr := Alloc ("+");
|
||||
Pr_Str : constant Ptr := Alloc ("pr-str");
|
||||
Println : constant Ptr := Alloc ("println");
|
||||
Prn : constant Ptr := Alloc ("prn");
|
||||
Quasiquote : constant Ptr := Alloc ("quasiquote");
|
||||
Quote : constant Ptr := Alloc ("quote");
|
||||
Read_String : constant Ptr := Alloc ("read-string");
|
||||
Readline : constant Ptr := Alloc ("readline");
|
||||
Reset : constant Ptr := Alloc ("reset!");
|
||||
Rest : constant Ptr := Alloc ("rest");
|
||||
Seq : constant Ptr := Alloc ("seq");
|
||||
Slash : constant Ptr := Alloc ("/");
|
||||
Slurp : constant Ptr := Alloc ("slurp");
|
||||
Splice_Unquote : constant Ptr := Alloc ("splice-unquote");
|
||||
Str : constant Ptr := Alloc ("str");
|
||||
Swap : constant Ptr := Alloc ("swap!");
|
||||
Symbol : constant Ptr := Alloc ("symbol");
|
||||
Throw : constant Ptr := Alloc ("throw");
|
||||
Time_Ms : constant Ptr := Alloc ("time-ms");
|
||||
Try : constant Ptr := Alloc ("try*");
|
||||
Unquote : constant Ptr := Alloc ("unquote");
|
||||
Vals : constant Ptr := Alloc ("vals");
|
||||
Vector : constant Ptr := Alloc ("vector");
|
||||
With_Meta : constant Ptr := Alloc ("with-meta");
|
||||
|
||||
end Names;
|
171
ada2/printer.adb
171
ada2/printer.adb
@ -1,55 +1,48 @@
|
||||
with Ada.Characters.Latin_1;
|
||||
with Atoms;
|
||||
with Lists;
|
||||
with Maps;
|
||||
with Strings;
|
||||
|
||||
with Types.Atoms;
|
||||
with Types.Functions;
|
||||
with Types.Lists;
|
||||
with Types.Maps;
|
||||
|
||||
package body Printer is
|
||||
|
||||
use Ada.Strings.Unbounded;
|
||||
use Types;
|
||||
|
||||
procedure Print_Form (Buffer : in out Unbounded_String;
|
||||
Ast : in Mal_Type;
|
||||
Print_Readably : in Boolean);
|
||||
procedure Print_List (Buffer : in out Unbounded_String;
|
||||
List : in Lists.Ptr;
|
||||
Print_Readably : in Boolean)
|
||||
with Inline;
|
||||
procedure Print_Function (Buffer : in out Unbounded_String;
|
||||
Formals : in Lists.Ptr;
|
||||
Expression : in Atoms.Ptr;
|
||||
Print_Readably : in Boolean)
|
||||
with Inline;
|
||||
procedure Print_Map (Buffer : in out Unbounded_String;
|
||||
Map : in Maps.Ptr;
|
||||
Print_Readably : in Boolean)
|
||||
with Inline;
|
||||
procedure Print_Form (Buffer : in out Unbounded_String;
|
||||
Ast : in Mal.T;
|
||||
Readably : in Boolean);
|
||||
procedure Print_List (Buffer : in out Unbounded_String;
|
||||
List : in Lists.Ptr;
|
||||
Readably : in Boolean) with Inline;
|
||||
procedure Print_Function (Buffer : in out Unbounded_String;
|
||||
Fn : in Functions.Ptr;
|
||||
Readably : in Boolean) with Inline;
|
||||
procedure Print_Map (Buffer : in out Unbounded_String;
|
||||
Map : in Maps.Ptr;
|
||||
Readably : in Boolean) with Inline;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
procedure Print_Form (Buffer : in out Unbounded_String;
|
||||
Ast : in Mal_Type;
|
||||
Print_Readably : in Boolean) is
|
||||
procedure Print_Form (Buffer : in out Unbounded_String;
|
||||
Ast : in Mal.T;
|
||||
Readably : in Boolean) is
|
||||
begin
|
||||
case Ast.Kind is
|
||||
|
||||
when Kind_Nil =>
|
||||
Append (Buffer, "nil");
|
||||
|
||||
when Kind_Boolean =>
|
||||
if Ast.Boolean_Value then
|
||||
if Ast.Ada_Boolean then
|
||||
Append (Buffer, "true");
|
||||
else
|
||||
Append (Buffer, "false");
|
||||
end if;
|
||||
|
||||
when Kind_Symbol =>
|
||||
Append (Buffer, Ast.S.Deref);
|
||||
|
||||
Append (Buffer, Ast.Symbol.To_String);
|
||||
when Kind_Number =>
|
||||
declare
|
||||
Img : constant String := Integer'Image (Ast.Integer_Value);
|
||||
Img : constant String := Ast.Ada_Number'Img;
|
||||
F : Positive := Img'First;
|
||||
begin
|
||||
if Img (F) = ' ' then
|
||||
@ -57,121 +50,113 @@ package body Printer is
|
||||
end if;
|
||||
Append (Buffer, Img (F .. Img'Last));
|
||||
end;
|
||||
|
||||
when Kind_Keyword =>
|
||||
Append (Buffer, ':');
|
||||
Append (Buffer, Ast.S.Deref);
|
||||
|
||||
Append (Buffer, Ast.S);
|
||||
when Kind_String =>
|
||||
if Print_Readably then
|
||||
if Readably then
|
||||
Append (Buffer, '"');
|
||||
for C of Ast.S.Deref loop
|
||||
case C is
|
||||
when '"' | '\' =>
|
||||
Append (Buffer, '\');
|
||||
Append (Buffer, C);
|
||||
when Ada.Characters.Latin_1.LF =>
|
||||
Append (Buffer, "\n");
|
||||
when others =>
|
||||
Append (Buffer, C);
|
||||
end case;
|
||||
end loop;
|
||||
declare
|
||||
C : Character;
|
||||
begin
|
||||
for I in 1 .. Length (Ast.S) loop
|
||||
C := Element (Ast.S, I);
|
||||
case C is
|
||||
when '"' | '\' =>
|
||||
Append (Buffer, '\');
|
||||
Append (Buffer, C);
|
||||
when Ada.Characters.Latin_1.LF =>
|
||||
Append (Buffer, "\n");
|
||||
when others =>
|
||||
Append (Buffer, C);
|
||||
end case;
|
||||
end loop;
|
||||
end;
|
||||
Append (Buffer, '"');
|
||||
else
|
||||
Append (Buffer, Ast.S.Deref);
|
||||
Append (Buffer, Ast.S);
|
||||
end if;
|
||||
|
||||
when Kind_List =>
|
||||
Append (Buffer, '(');
|
||||
Print_List (Buffer, Ast.L, Print_Readably);
|
||||
Print_List (Buffer, Ast.L, Readably);
|
||||
Append (Buffer, ')');
|
||||
when Kind_Vector =>
|
||||
Append (Buffer, '[');
|
||||
Print_List (Buffer, Ast.L, Print_Readably);
|
||||
Print_List (Buffer, Ast.L, Readably);
|
||||
Append (Buffer, ']');
|
||||
|
||||
when Kind_Map =>
|
||||
Print_Map (Buffer, Ast.Map, Print_Readably);
|
||||
|
||||
when Kind_Native =>
|
||||
Print_Map (Buffer, Ast.Map, Readably);
|
||||
when Kind_Builtin | Kind_Builtin_With_Meta =>
|
||||
Append (Buffer, "#<built-in>");
|
||||
when Kind_Function =>
|
||||
Append (Buffer, "#<function ");
|
||||
Print_Function (Buffer, Ast.Formals, Ast.Expression,
|
||||
Print_Readably);
|
||||
Print_Function (Buffer, Ast.Function_Value, Readably);
|
||||
Append (Buffer, '>');
|
||||
when Kind_Macro =>
|
||||
Append (Buffer, "#<macro ");
|
||||
Print_Function (Buffer, Ast.Mac_Formals, Ast.Mac_Expression,
|
||||
Print_Readably);
|
||||
Print_Function (Buffer, Ast.Function_Value, Readably);
|
||||
Append (Buffer, '>');
|
||||
|
||||
when Kind_Atom =>
|
||||
Append (Buffer, "(atom ");
|
||||
Print_Form (Buffer, Ast.Reference.Deref, Print_Readably);
|
||||
Print_Form (Buffer, Atoms.Deref (Mal.T_Array'(1 => Ast)),
|
||||
Readably);
|
||||
Append (Buffer, ')');
|
||||
|
||||
end case;
|
||||
end Print_Form;
|
||||
|
||||
procedure Print_Function (Buffer : in out Unbounded_String;
|
||||
Formals : in Lists.Ptr;
|
||||
Expression : in Atoms.Ptr;
|
||||
Print_Readably : in Boolean) is
|
||||
procedure Print_Function (Buffer : in out Unbounded_String;
|
||||
Fn : in Functions.Ptr;
|
||||
Readably : in Boolean) is
|
||||
begin
|
||||
if 0 < Formals.Length then
|
||||
Print_List (Buffer, Formals, Print_Readably);
|
||||
Append (Buffer, " -> ");
|
||||
Print_Form (Buffer, Expression.Deref, Print_Readably);
|
||||
end if;
|
||||
Print_List (Buffer, Fn.Formals, Readably);
|
||||
Append (Buffer, " -> ");
|
||||
Print_Form (Buffer, Fn.Expression, Readably);
|
||||
end Print_Function;
|
||||
|
||||
procedure Print_List (Buffer : in out Unbounded_String;
|
||||
List : in Lists.Ptr;
|
||||
Print_Readably : in Boolean) is
|
||||
procedure Print_List (Buffer : in out Unbounded_String;
|
||||
List : in Lists.Ptr;
|
||||
Readably : in Boolean) is
|
||||
begin
|
||||
if 1 <= List.Length then
|
||||
Print_Form (Buffer, List.Element (1), Print_Readably);
|
||||
if 0 < List.Length then
|
||||
Print_Form (Buffer, List.Element (1), Readably);
|
||||
for I in 2 .. List.Length loop
|
||||
Append (Buffer, ' ');
|
||||
Print_Form (Buffer, List.Element (I), Print_Readably);
|
||||
Print_Form (Buffer, List.Element (I), Readably);
|
||||
end loop;
|
||||
end if;
|
||||
end Print_List;
|
||||
|
||||
procedure Print_Map (Buffer : in out Unbounded_String;
|
||||
Map : in Maps.Ptr;
|
||||
Print_Readably : in Boolean)
|
||||
is
|
||||
procedure Print_Map (Buffer : in out Unbounded_String;
|
||||
Map : in Maps.Ptr;
|
||||
Readably : in Boolean) is
|
||||
Is_First : Boolean := True;
|
||||
procedure Process (Key : in Mal_Type;
|
||||
Element : in Mal_Type);
|
||||
procedure Process (Key : in Mal_Type;
|
||||
Element : in Mal_Type) is
|
||||
procedure Process (Key : in Mal.T;
|
||||
Element : in Mal.T);
|
||||
procedure Iterate is new Maps.Iterate (Process);
|
||||
procedure Process (Key : in Mal.T;
|
||||
Element : in Mal.T) is
|
||||
begin
|
||||
if Is_First then
|
||||
Is_First := False;
|
||||
else
|
||||
Append (Buffer, ' ');
|
||||
end if;
|
||||
Print_Form (Buffer, Key, Print_Readably);
|
||||
Print_Form (Buffer, Key, Readably);
|
||||
Append (Buffer, ' ');
|
||||
Print_Form (Buffer, Element, Print_Readably);
|
||||
Print_Form (Buffer, Element, Readably);
|
||||
end Process;
|
||||
begin
|
||||
Append (Buffer, '{');
|
||||
Map.Iterate (Process'Access);
|
||||
Iterate (Map);
|
||||
Append (Buffer, '}');
|
||||
end Print_Map;
|
||||
|
||||
function Pr_Str (Ast : in Mal_Type;
|
||||
Print_Readably : in Boolean := True)
|
||||
return Unbounded_String
|
||||
is
|
||||
Result : Unbounded_String;
|
||||
function Pr_Str (Ast : in Mal.T;
|
||||
Readably : in Boolean := True) return Unbounded_String is
|
||||
begin
|
||||
Print_Form (Result, Ast, Print_Readably);
|
||||
return Result;
|
||||
return Buffer : Unbounded_String do
|
||||
Print_Form (Buffer, Ast, Readably);
|
||||
end return;
|
||||
end Pr_Str;
|
||||
|
||||
end Printer;
|
||||
|
@ -1,12 +1,11 @@
|
||||
with Ada.Strings.Unbounded;
|
||||
with Types;
|
||||
|
||||
package Printer is
|
||||
with Types.Mal;
|
||||
|
||||
pragma Elaborate_Body;
|
||||
package Printer with Elaborate_Body is
|
||||
|
||||
function Pr_Str (Ast : in Types.Mal_Type;
|
||||
Print_Readably : in Boolean := True)
|
||||
function Pr_Str (Ast : in Types.Mal.T;
|
||||
Readably : in Boolean := True)
|
||||
return Ada.Strings.Unbounded.Unbounded_String;
|
||||
|
||||
end Printer;
|
||||
|
140
ada2/reader.adb
140
ada2/reader.adb
@ -1,18 +1,19 @@
|
||||
with Ada.Characters.Latin_1;
|
||||
with Atoms;
|
||||
with Lists;
|
||||
with Maps;
|
||||
with Names;
|
||||
with Strings;
|
||||
with Ada.Strings.Unbounded;
|
||||
|
||||
with Types.Lists;
|
||||
with Types.Maps;
|
||||
with Types.Symbols.Names;
|
||||
|
||||
package body Reader is
|
||||
|
||||
function Read_Str (Source : in String) return Types.Mal_Type
|
||||
is
|
||||
use Types;
|
||||
|
||||
function Read_Str (Source : in String) return Mal.T is
|
||||
First : Positive;
|
||||
Last : Natural := Source'First - 1;
|
||||
|
||||
function Read_Form return Types.Mal_Type;
|
||||
function Read_Form return Mal.T;
|
||||
|
||||
procedure Find_Next_Token;
|
||||
-- Search next token from index Last + 1.
|
||||
@ -24,10 +25,9 @@ package body Reader is
|
||||
|
||||
-- Read_Atom has been merged into the same case/switch
|
||||
-- statement, for clarity and efficiency.
|
||||
function Read_List (Ending : in Character) return Types.Mal_Type_Array
|
||||
with Inline;
|
||||
function Read_Quote (Symbol : in Strings.Ptr) return Types.Mal_Type
|
||||
function Read_List (Ending : in Character) return Mal.T_Array
|
||||
with Inline;
|
||||
function Read_Quote (Symbol : in Symbols.Ptr) return Mal.T with Inline;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
procedure Find_Next_Token
|
||||
@ -61,7 +61,7 @@ package body Reader is
|
||||
Last := First + 1;
|
||||
loop
|
||||
if Source'Last < Last then
|
||||
raise Reader_Error with "expected '""'";
|
||||
raise Reader_Error with "unbalanced '""'";
|
||||
end if;
|
||||
exit when Source (Last) = '"';
|
||||
if Source (Last) = '\' then
|
||||
@ -96,126 +96,108 @@ package body Reader is
|
||||
end loop;
|
||||
end Find_Next_Token;
|
||||
|
||||
function Read_Form return Types.Mal_Type
|
||||
is
|
||||
use Types;
|
||||
function Read_Form return Mal.T is
|
||||
use Ada.Strings.Unbounded;
|
||||
begin
|
||||
case Source (First) is
|
||||
|
||||
when '(' =>
|
||||
return (Kind_List, Atoms.No_Element,
|
||||
Lists.Alloc (Read_List (')')));
|
||||
return Lists.List (Read_List (')'));
|
||||
when '[' =>
|
||||
return (Kind_Vector, Atoms.No_Element,
|
||||
Lists.Alloc (Read_List (']')));
|
||||
return Lists.Vector (Read_List (']'));
|
||||
when '{' =>
|
||||
return (Kind_Map, Atoms.No_Element,
|
||||
Maps.Hash_Map (Read_List ('}')));
|
||||
|
||||
return Maps.Hash_Map (Read_List ('}'));
|
||||
when '"' =>
|
||||
declare
|
||||
Buffer : String (First .. Last);
|
||||
B_Last : Natural := Buffer'First - 1;
|
||||
Buffer : Unbounded_String;
|
||||
I : Positive := First + 1;
|
||||
begin
|
||||
while I <= Last - 1 loop
|
||||
if Source (I) /= '\' or else I = Last - 1 then
|
||||
B_Last := B_Last + 1;
|
||||
Buffer (B_Last) := Source (I);
|
||||
while I < Last loop
|
||||
if Source (I) /= '\' or else I + 1 = Last then
|
||||
Append (Buffer, Source (I));
|
||||
else
|
||||
case Source (I + 1) is
|
||||
when '\' | '"' =>
|
||||
B_Last := B_Last + 1;
|
||||
Buffer (B_Last) := Source (I + 1);
|
||||
I := I + 1;
|
||||
Append (Buffer, Source (I));
|
||||
when 'n' =>
|
||||
B_Last := B_Last + 1;
|
||||
Buffer (B_Last) := Ada.Characters.Latin_1.LF;
|
||||
I := I + 1;
|
||||
Append (Buffer, Ada.Characters.Latin_1.LF);
|
||||
when others =>
|
||||
B_Last := B_Last + 1;
|
||||
Buffer (B_Last) := Source (I);
|
||||
Append (Buffer, Source (I));
|
||||
end case;
|
||||
end if;
|
||||
I := I + 1;
|
||||
end loop;
|
||||
return (Kind_String, Atoms.No_Element,
|
||||
Strings.Alloc (Buffer (Buffer'First .. B_Last)));
|
||||
return (Kind_String, Buffer);
|
||||
end;
|
||||
when ':' =>
|
||||
return (Kind_Keyword, Atoms.No_Element,
|
||||
Strings.Alloc (Source (First + 1 .. Last)));
|
||||
|
||||
return (Kind_Keyword,
|
||||
To_Unbounded_String (Source (First + 1 .. Last)));
|
||||
when '-' =>
|
||||
if First < Last
|
||||
and then (for all C of Source (First + 1 .. Last) =>
|
||||
C in '0' .. '9')
|
||||
then
|
||||
return (Kind_Number, Atoms.No_Element,
|
||||
Integer'Value (Source (First .. Last)));
|
||||
return (Kind_Number, Integer'Value (Source (First .. Last)));
|
||||
else
|
||||
return (Kind_Symbol, Atoms.No_Element,
|
||||
Strings.Alloc (Source (First .. Last)));
|
||||
return (Kind_Symbol,
|
||||
Symbols.Constructor (Source (First .. Last)));
|
||||
end if;
|
||||
when '0' .. '9' =>
|
||||
return (Kind_Number, Atoms.No_Element,
|
||||
Integer'Value (Source (First .. Last)));
|
||||
|
||||
return (Kind_Number, Integer'Value (Source (First .. Last)));
|
||||
when ''' =>
|
||||
return Read_Quote (Names.Quote);
|
||||
return Read_Quote (Symbols.Names.Quote);
|
||||
when '`' =>
|
||||
return Read_Quote (Names.Quasiquote);
|
||||
return Read_Quote (Symbols.Names.Quasiquote);
|
||||
when '@' =>
|
||||
return Read_Quote (Names.Deref);
|
||||
return Read_Quote (Symbols.Names.Deref);
|
||||
when '~' =>
|
||||
if First = Last then
|
||||
return Read_Quote (Names.Unquote);
|
||||
return Read_Quote (Symbols.Names.Unquote);
|
||||
else
|
||||
return Read_Quote (Names.Splice_Unquote);
|
||||
return Read_Quote (Symbols.Names.Splice_Unquote);
|
||||
end if;
|
||||
when '^' =>
|
||||
return Result : constant Mal_Type
|
||||
:= (Kind_List, Atoms.No_Element, Lists.Alloc (3))
|
||||
do
|
||||
Result.L.Replace_Element (1, Mal_Type'
|
||||
(Kind_Symbol, Atoms.No_Element, Names.With_Meta));
|
||||
declare
|
||||
Args : Mal.T_Array (1 .. 3);
|
||||
begin
|
||||
Args (1) := (Kind_Symbol, Symbols.Names.With_Meta);
|
||||
Find_Next_Token;
|
||||
if Source'Last < First then
|
||||
raise Reader_Error with "Unfinished 'with-meta'";
|
||||
end if;
|
||||
Result.L.Replace_Element (3, Read_Form);
|
||||
Args (3) := Read_Form;
|
||||
Find_Next_Token;
|
||||
if Source'Last < First then
|
||||
raise Reader_Error with "Unfinished 'with-meta'";
|
||||
end if;
|
||||
Result.L.Replace_Element (2, Read_Form);
|
||||
end return;
|
||||
|
||||
Args (2) := Read_Form;
|
||||
return Lists.List (Args);
|
||||
end;
|
||||
when others =>
|
||||
if Source (First .. Last) = "nil" then
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
if Source (First .. Last) = "false" then
|
||||
return (Kind_Boolean, False);
|
||||
elsif Source (First .. Last) = "nil" then
|
||||
return Mal.Nil;
|
||||
elsif Source (First .. Last) = "true" then
|
||||
return (Kind_Boolean, Atoms.No_Element, True);
|
||||
elsif Source (First .. Last) = "false" then
|
||||
return (Kind_Boolean, Atoms.No_Element, False);
|
||||
return (Kind_Boolean, True);
|
||||
else
|
||||
return (Kind_Symbol, Atoms.No_Element,
|
||||
Strings.Alloc (Source (First .. Last)));
|
||||
return (Kind_Symbol,
|
||||
Symbols.Constructor (Source (First .. Last)));
|
||||
end if;
|
||||
end case;
|
||||
end Read_Form;
|
||||
|
||||
function Read_List (Ending : in Character) return Types.Mal_Type_Array
|
||||
is
|
||||
function Read_List (Ending : in Character) return Mal.T_Array is
|
||||
-- Using big arrays on the stack is faster than doing
|
||||
-- repeated dynamic reallocations.
|
||||
Buffer : Types.Mal_Type_Array (First + 1 .. Source'Last);
|
||||
Buffer : Mal.T_Array (First + 1 .. Source'Last);
|
||||
B_Last : Natural := Buffer'First - 1;
|
||||
begin
|
||||
loop
|
||||
Find_Next_Token;
|
||||
if Source'Last < First then
|
||||
raise Reader_Error with "expected '" & Ending & "'";
|
||||
raise Reader_Error with "unbalanced '" & Ending & "'";
|
||||
end if;
|
||||
exit when Source (First) = Ending;
|
||||
B_Last := B_Last + 1;
|
||||
@ -224,19 +206,13 @@ package body Reader is
|
||||
return Buffer (Buffer'First .. B_Last);
|
||||
end Read_List;
|
||||
|
||||
function Read_Quote (Symbol : in Strings.Ptr) return Types.Mal_Type is
|
||||
use Types;
|
||||
Result : constant Mal_Type
|
||||
:= (Kind_List, Atoms.No_Element, Lists.Alloc (2));
|
||||
function Read_Quote (Symbol : in Symbols.Ptr) return Mal.T is
|
||||
begin
|
||||
Result.L.Replace_Element (1,
|
||||
Mal_Type'(Kind_Symbol, Atoms.No_Element, Symbol));
|
||||
Find_Next_Token;
|
||||
if Source'Last < First then
|
||||
raise Reader_Error with "Unfinished '" & Symbol.Deref & "'";
|
||||
raise Reader_Error with "Unfinished '" & Symbol.To_String & "'";
|
||||
end if;
|
||||
Result.L.Replace_Element (2, Read_Form);
|
||||
return Result;
|
||||
return Lists.List (Mal.T_Array'((Kind_Symbol, Symbol), Read_Form));
|
||||
end Read_Quote;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
@ -244,7 +220,7 @@ package body Reader is
|
||||
begin
|
||||
Find_Next_Token;
|
||||
if Source'Last < First then
|
||||
raise Empty_Source;
|
||||
raise Empty_Source with "attempting to read an empty line";
|
||||
end if;
|
||||
return Read_Form;
|
||||
end Read_Str;
|
||||
|
@ -1,10 +1,8 @@
|
||||
with Types;
|
||||
with Types.Mal;
|
||||
|
||||
package Reader is
|
||||
package Reader with Elaborate_Body is
|
||||
|
||||
pragma Elaborate_Body;
|
||||
|
||||
function Read_Str (Source : in String) return Types.Mal_Type;
|
||||
function Read_Str (Source : in String) return Types.Mal.T;
|
||||
|
||||
Empty_Source : exception;
|
||||
Reader_Error : exception;
|
||||
|
@ -1,57 +1,48 @@
|
||||
with Ada.Exceptions;
|
||||
with Ada.Text_IO;
|
||||
with Interfaces.C.Strings; use type Interfaces.C.Strings.chars_ptr;
|
||||
with Interfaces.C.Strings;
|
||||
|
||||
procedure Step0_Repl is
|
||||
|
||||
function Read (Source : in String) return String
|
||||
is (Source);
|
||||
subtype Mal_Type is String;
|
||||
|
||||
function Eval (Ast : in String) return String
|
||||
is (Ast);
|
||||
function Read (Source : in String) return Mal_Type
|
||||
is (Source);
|
||||
|
||||
function Print (Ast : in String) return String
|
||||
is (Ast);
|
||||
function Eval (Ast : in Mal_Type) return Mal_Type
|
||||
is (Ast);
|
||||
|
||||
function Print (Ast : in Mal_Type) return String
|
||||
is (Ast);
|
||||
|
||||
function Rep (Source : in String) return String
|
||||
is (Print (Eval (Read (Source))));
|
||||
is (Print (Eval (Read (Source)))) with Inline;
|
||||
|
||||
procedure Interactive_Loop;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
procedure Interactive_Loop
|
||||
is
|
||||
|
||||
function Readline (Prompt : in Interfaces.C.char_array)
|
||||
return Interfaces.C.Strings.chars_ptr
|
||||
procedure Interactive_Loop is
|
||||
use Interfaces.C, Interfaces.C.Strings;
|
||||
function Readline (Prompt : in char_array) return chars_ptr
|
||||
with Import, Convention => C, External_Name => "readline";
|
||||
|
||||
procedure Add_History (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Add_History (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "add_history";
|
||||
|
||||
procedure Free (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Free (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "free";
|
||||
|
||||
Prompt : constant Interfaces.C.char_array
|
||||
:= Interfaces.C.To_C ("user> ");
|
||||
C_Line : Interfaces.C.Strings.chars_ptr;
|
||||
Prompt : constant char_array := To_C ("user> ");
|
||||
C_Line : chars_ptr;
|
||||
begin
|
||||
loop
|
||||
C_Line := Readline (Prompt);
|
||||
exit when C_Line = Interfaces.C.Strings.Null_Ptr;
|
||||
exit when C_Line = Null_Ptr;
|
||||
declare
|
||||
Line : constant String := Interfaces.C.Strings.Value (C_Line);
|
||||
Line : constant String := Value (C_Line);
|
||||
begin
|
||||
if Line /= "" then
|
||||
Add_History (C_Line);
|
||||
end if;
|
||||
Free (C_Line);
|
||||
Ada.Text_IO.Put_Line (Rep (Line));
|
||||
exception
|
||||
when E : others =>
|
||||
Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
|
||||
-- but go on proceeding.
|
||||
end;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
|
@ -1,56 +1,50 @@
|
||||
with Ada.Exceptions;
|
||||
with Ada.Strings.Unbounded;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
with Interfaces.C.Strings; use type Interfaces.C.Strings.chars_ptr;
|
||||
with Interfaces.C.Strings;
|
||||
|
||||
with Printer;
|
||||
with Reader;
|
||||
with Types;
|
||||
with Types.Mal;
|
||||
|
||||
procedure Step1_Read_Print is
|
||||
|
||||
function Read (Source : in String) return Types.Mal_Type
|
||||
package ASU renames Ada.Strings.Unbounded;
|
||||
use Types;
|
||||
|
||||
function Read (Source : in String) return Mal.T
|
||||
renames Reader.Read_Str;
|
||||
|
||||
function Eval (Ast : in Types.Mal_Type) return Types.Mal_Type
|
||||
is (Ast);
|
||||
function Eval (Ast : in Mal.T) return Mal.T
|
||||
is (Ast);
|
||||
|
||||
function Print (Ast : in Types.Mal_Type;
|
||||
Print_Readably : in Boolean := True)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
function Print (Ast : in Mal.T;
|
||||
Readably : in Boolean := True) return ASU.Unbounded_String
|
||||
renames Printer.Pr_Str;
|
||||
|
||||
function Rep (Source : in String)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
is (Print (Eval (Read (Source))))
|
||||
with Inline;
|
||||
function Rep (Source : in String) return ASU.Unbounded_String
|
||||
is (Print (Eval (Read (Source)))) with Inline;
|
||||
|
||||
procedure Interactive_Loop
|
||||
with Inline;
|
||||
procedure Interactive_Loop;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
procedure Interactive_Loop
|
||||
is
|
||||
|
||||
function Readline (Prompt : in Interfaces.C.char_array)
|
||||
return Interfaces.C.Strings.chars_ptr
|
||||
procedure Interactive_Loop is
|
||||
use Interfaces.C, Interfaces.C.Strings;
|
||||
function Readline (Prompt : in char_array) return chars_ptr
|
||||
with Import, Convention => C, External_Name => "readline";
|
||||
|
||||
procedure Add_History (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Add_History (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "add_history";
|
||||
|
||||
procedure Free (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Free (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "free";
|
||||
|
||||
Prompt : constant Interfaces.C.char_array
|
||||
:= Interfaces.C.To_C ("user> ");
|
||||
C_Line : Interfaces.C.Strings.chars_ptr;
|
||||
Prompt : constant char_array := To_C ("user> ");
|
||||
C_Line : chars_ptr;
|
||||
begin
|
||||
loop
|
||||
C_Line := Readline (Prompt);
|
||||
exit when C_Line = Interfaces.C.Strings.Null_Ptr;
|
||||
exit when C_Line = Null_Ptr;
|
||||
declare
|
||||
Line : constant String := Interfaces.C.Strings.Value (C_Line);
|
||||
Line : constant String := Value (C_Line);
|
||||
begin
|
||||
if Line /= "" then
|
||||
Add_History (C_Line);
|
||||
@ -60,9 +54,9 @@ procedure Step1_Read_Print is
|
||||
exception
|
||||
when Reader.Empty_Source =>
|
||||
null;
|
||||
when E : others =>
|
||||
when E : Reader.Reader_Error =>
|
||||
Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
|
||||
-- but go on proceeding.
|
||||
-- Other exceptions are unexpected.
|
||||
end;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
|
@ -3,142 +3,122 @@ with Ada.Exceptions;
|
||||
with Ada.Strings.Hash;
|
||||
with Ada.Strings.Unbounded;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
with Interfaces.C.Strings; use type Interfaces.C.Strings.chars_ptr;
|
||||
with Atoms;
|
||||
with Lists;
|
||||
with Interfaces.C.Strings;
|
||||
|
||||
with Printer;
|
||||
with Reader;
|
||||
with Types;
|
||||
with Types.Builtins;
|
||||
with Types.Lists;
|
||||
with Types.Mal;
|
||||
with Types.Maps;
|
||||
|
||||
procedure Step2_Eval is
|
||||
|
||||
package ASU renames Ada.Strings.Unbounded;
|
||||
use Types;
|
||||
|
||||
package Environments is new Ada.Containers.Indefinite_Hashed_Maps
|
||||
(Key_Type => String,
|
||||
Element_Type => Types.Native_Function_Access,
|
||||
Element_Type => Builtins.Ptr,
|
||||
Hash => Ada.Strings.Hash,
|
||||
Equivalent_Keys => "=",
|
||||
"=" => Types."=");
|
||||
|
||||
function Read (Source : in String) return Types.Mal_Type
|
||||
renames Reader.Read_Str;
|
||||
|
||||
function Eval (Ast : in Types.Mal_Type;
|
||||
Env : in out Environments.Map) return Types.Mal_Type;
|
||||
Unable_To_Call : exception;
|
||||
"=" => Builtins."=");
|
||||
Unknown_Symbol : exception;
|
||||
|
||||
function Print (Ast : in Types.Mal_Type;
|
||||
Print_Readably : in Boolean := True)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
function Read (Source : in String) return Mal.T
|
||||
renames Reader.Read_Str;
|
||||
|
||||
function Eval (Ast : in Mal.T;
|
||||
Env : in Environments.Map) return Mal.T;
|
||||
|
||||
function Print (Ast : in Mal.T;
|
||||
Readably : in Boolean := True) return ASU.Unbounded_String
|
||||
renames Printer.Pr_Str;
|
||||
|
||||
function Rep (Source : in String;
|
||||
Env : in out Environments.Map)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env)))
|
||||
with Inline;
|
||||
function Rep (Source : in String;
|
||||
Env : in Environments.Map) return ASU.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env))) with Inline;
|
||||
|
||||
procedure Interactive_Loop (Repl : in out Environments.Map)
|
||||
with Inline;
|
||||
procedure Interactive_Loop (Repl : in Environments.Map);
|
||||
|
||||
generic
|
||||
with function Ada_Operator (Left, Right : in Integer) return Integer;
|
||||
function Generic_Mal_Operator (Args : in Types.Mal_Type_Array)
|
||||
return Types.Mal_Type;
|
||||
function Generic_Mal_Operator (Args : in Mal.T_Array) return Mal.T;
|
||||
|
||||
function Eval_Elements is new Lists.Generic_Eval (Environments.Map, Eval);
|
||||
function Eval_Elements is new Maps.Generic_Eval (Environments.Map, Eval);
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function Eval (Ast : in Types.Mal_Type;
|
||||
Env : in out Environments.Map) return Types.Mal_Type
|
||||
is
|
||||
use Types;
|
||||
function Eval (Ast : in Mal.T;
|
||||
Env : in Environments.Map) return Mal.T is
|
||||
First : Mal.T;
|
||||
begin
|
||||
-- Ada.Text_IO.New_Line;
|
||||
-- Ada.Text_IO.Put ("EVAL: ");
|
||||
-- Ada.Text_IO.Unbounded_IO.Put_Line (Print (Ast));
|
||||
case Ast.Kind is
|
||||
when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Kind_String
|
||||
| Kind_Keyword | Kind_Macro | Kind_Function | Kind_Native =>
|
||||
return Ast;
|
||||
|
||||
when Kind_Symbol =>
|
||||
declare
|
||||
S : constant String := Ast.S.Deref;
|
||||
S : constant String := Ast.Symbol.To_String;
|
||||
C : constant Environments.Cursor := Env.Find (S);
|
||||
begin
|
||||
if Environments.Has_Element (C) then
|
||||
return (Kind_Native, Atoms.No_Element,
|
||||
Environments.Element (C));
|
||||
return (Kind_Builtin, Environments.Element (C));
|
||||
else
|
||||
-- The predefined message does not pass tests.
|
||||
raise Unknown_Symbol with "'" & S & "' not found";
|
||||
end if;
|
||||
end;
|
||||
|
||||
when Kind_Map =>
|
||||
declare
|
||||
function F (X : Mal_Type) return Mal_Type is (Eval (X, Env));
|
||||
begin
|
||||
return (Kind_Map, Atoms.No_Element, Ast.Map.Map (F'Access));
|
||||
end;
|
||||
|
||||
return Eval_Elements (Ast.Map, Env);
|
||||
when Kind_Vector =>
|
||||
return R : constant Mal_Type := (Kind_Vector, Atoms.No_Element,
|
||||
Lists.Alloc (Ast.L.Length))
|
||||
do
|
||||
for I in 1 .. Ast.L.Length loop
|
||||
R.L.Replace_Element (I, Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
end return;
|
||||
|
||||
return (Kind_Vector, Eval_Elements (Ast.L, Env));
|
||||
when Kind_List =>
|
||||
if Ast.L.Length = 0 then
|
||||
return Ast;
|
||||
end if;
|
||||
|
||||
-- Apply phase
|
||||
declare
|
||||
First : constant Mal_Type := Eval (Ast.L.Element (1), Env);
|
||||
Args : Mal_Type_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
case First.Kind is
|
||||
when Kind_Native =>
|
||||
return First.Native.all (Args);
|
||||
when others =>
|
||||
raise Unable_To_Call
|
||||
with Ada.Strings.Unbounded.To_String (Print (First));
|
||||
end case;
|
||||
end;
|
||||
First := Eval (Ast.L.Element (1), Env);
|
||||
-- Apply phase.
|
||||
case First.Kind is
|
||||
when Kind_Builtin =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
return First.Builtin.all (Args);
|
||||
end;
|
||||
when others =>
|
||||
raise Argument_Error
|
||||
with "cannot execute " & ASU.To_String (Print (First));
|
||||
end case;
|
||||
when others =>
|
||||
return Ast;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
function Generic_Mal_Operator (Args : in Types.Mal_Type_Array)
|
||||
return Types.Mal_Type
|
||||
is (Types.Kind_Number, Atoms.No_Element,
|
||||
Ada_Operator (Args (Args'First).Integer_Value,
|
||||
Args (Args'First + 1).Integer_Value));
|
||||
function Generic_Mal_Operator (Args : in Mal.T_Array) return Mal.T
|
||||
is (Kind_Number, Ada_Operator (Args (Args'First).Ada_Number,
|
||||
Args (Args'Last).Ada_Number));
|
||||
|
||||
procedure Interactive_Loop (Repl : in out Environments.Map)
|
||||
is
|
||||
|
||||
function Readline (Prompt : in Interfaces.C.char_array)
|
||||
return Interfaces.C.Strings.chars_ptr
|
||||
procedure Interactive_Loop (Repl : in Environments.Map) is
|
||||
use Interfaces.C, Interfaces.C.Strings;
|
||||
function Readline (Prompt : in char_array) return chars_ptr
|
||||
with Import, Convention => C, External_Name => "readline";
|
||||
|
||||
procedure Add_History (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Add_History (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "add_history";
|
||||
|
||||
procedure Free (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Free (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "free";
|
||||
|
||||
Prompt : constant Interfaces.C.char_array
|
||||
:= Interfaces.C.To_C ("user> ");
|
||||
C_Line : Interfaces.C.Strings.chars_ptr;
|
||||
Prompt : constant char_array := To_C ("user> ");
|
||||
C_Line : chars_ptr;
|
||||
begin
|
||||
loop
|
||||
C_Line := Readline (Prompt);
|
||||
exit when C_Line = Interfaces.C.Strings.Null_Ptr;
|
||||
exit when C_Line = Null_Ptr;
|
||||
declare
|
||||
Line : constant String := Interfaces.C.Strings.Value (C_Line);
|
||||
Line : constant String := Value (C_Line);
|
||||
begin
|
||||
if Line /= "" then
|
||||
Add_History (C_Line);
|
||||
@ -148,9 +128,10 @@ procedure Step2_Eval is
|
||||
exception
|
||||
when Reader.Empty_Source =>
|
||||
null;
|
||||
when E : others =>
|
||||
when E : Argument_Error | Reader.Reader_Error
|
||||
| Unknown_Symbol =>
|
||||
Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
|
||||
-- but go on proceeding.
|
||||
-- Other exceptions are unexpected.
|
||||
end;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
@ -165,11 +146,10 @@ procedure Step2_Eval is
|
||||
|
||||
Repl : Environments.Map;
|
||||
begin
|
||||
Repl.Include ("+", Addition 'Unrestricted_Access);
|
||||
Repl.Include ("-", Subtraction'Unrestricted_Access);
|
||||
Repl.Include ("*", Product 'Unrestricted_Access);
|
||||
Repl.Include ("/", Division 'Unrestricted_Access);
|
||||
Repl.Insert ("+", Addition 'Unrestricted_Access);
|
||||
Repl.Insert ("-", Subtraction'Unrestricted_Access);
|
||||
Repl.Insert ("*", Product 'Unrestricted_Access);
|
||||
Repl.Insert ("/", Division 'Unrestricted_Access);
|
||||
|
||||
Interactive_Loop (Repl);
|
||||
pragma Unreferenced (Repl);
|
||||
end Step2_Eval;
|
||||
|
@ -1,163 +1,149 @@
|
||||
with Ada.Exceptions;
|
||||
with Ada.Strings.Unbounded;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
with Interfaces.C.Strings; use type Interfaces.C.Strings.chars_ptr;
|
||||
with Atoms;
|
||||
with Interfaces.C.Strings;
|
||||
|
||||
with Environments;
|
||||
with Lists;
|
||||
with Names;
|
||||
with Printer;
|
||||
with Reader;
|
||||
with Strings; use type Strings.Ptr;
|
||||
with Types;
|
||||
with Types.Lists;
|
||||
with Types.Mal;
|
||||
with Types.Maps;
|
||||
with Types.Symbols.Names;
|
||||
|
||||
procedure Step3_Env is
|
||||
|
||||
function Read (Source : in String) return Types.Mal_Type
|
||||
package ASU renames Ada.Strings.Unbounded;
|
||||
use Types;
|
||||
use type Symbols.Ptr;
|
||||
|
||||
function Read (Source : in String) return Mal.T
|
||||
renames Reader.Read_Str;
|
||||
|
||||
function Eval (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
Unable_To_Call : exception;
|
||||
function Eval (Ast : in Mal.T;
|
||||
Env : in Environments.Ptr) return Mal.T;
|
||||
|
||||
function Print (Ast : in Types.Mal_Type;
|
||||
Print_Readably : in Boolean := True)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
function Print (Ast : in Mal.T;
|
||||
Readably : in Boolean := True) return ASU.Unbounded_String
|
||||
renames Printer.Pr_Str;
|
||||
|
||||
function Rep (Source : in String;
|
||||
Env : in Environments.Ptr)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env)))
|
||||
with Inline;
|
||||
Env : in Environments.Ptr) return ASU.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env))) with Inline;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
with Inline;
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr);
|
||||
|
||||
generic
|
||||
with function Ada_Operator (Left, Right : in Integer) return Integer;
|
||||
function Generic_Mal_Operator (Args : in Types.Mal_Type_Array)
|
||||
return Types.Mal_Type;
|
||||
function Generic_Mal_Operator (Args : in Mal.T_Array) return Mal.T;
|
||||
|
||||
function Eval_Elements is new Lists.Generic_Eval (Environments.Ptr, Eval);
|
||||
function Eval_Elements is new Maps.Generic_Eval (Environments.Ptr, Eval);
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function Eval (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type
|
||||
is
|
||||
use Types;
|
||||
First : Mal_Type;
|
||||
function Eval (Ast : in Mal.T;
|
||||
Env : in Environments.Ptr) return Mal.T is
|
||||
First : Mal.T;
|
||||
begin
|
||||
-- Ada.Text_IO.New_Line;
|
||||
-- Ada.Text_IO.Put ("EVAL: ");
|
||||
-- Ada.Text_IO.Unbounded_IO.Put_Line (Print (Ast));
|
||||
-- Environments.Dump_Stack;
|
||||
case Ast.Kind is
|
||||
when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Kind_String
|
||||
| Kind_Keyword | Kind_Macro | Kind_Function | Kind_Native =>
|
||||
return Ast;
|
||||
|
||||
when Kind_Symbol =>
|
||||
return Env.Get (Ast.S);
|
||||
|
||||
return Env.Get (Ast.Symbol);
|
||||
when Kind_Map =>
|
||||
declare
|
||||
function F (X : Mal_Type) return Mal_Type is (Eval (X, Env));
|
||||
begin
|
||||
return (Kind_Map, Atoms.No_Element, Ast.Map.Map (F'Access));
|
||||
end;
|
||||
|
||||
return Eval_Elements (Ast.Map, Env);
|
||||
when Kind_Vector =>
|
||||
return R : constant Mal_Type := (Kind_Vector, Atoms.No_Element,
|
||||
Lists.Alloc (Ast.L.Length))
|
||||
do
|
||||
for I in 1 .. Ast.L.Length loop
|
||||
R.L.Replace_Element (I, Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
end return;
|
||||
|
||||
return (Kind_Vector, Eval_Elements (Ast.L, Env));
|
||||
when Kind_List =>
|
||||
if Ast.L.Length = 0 then
|
||||
return Ast;
|
||||
end if;
|
||||
|
||||
First := Ast.L.Element (1);
|
||||
|
||||
-- Special forms
|
||||
if First.Kind = Kind_Symbol then
|
||||
|
||||
if First.S = Names.Def then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (2).Kind = Kind_Symbol);
|
||||
return R : constant Mal_Type := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).S, R);
|
||||
end return;
|
||||
|
||||
elsif First.S = Names.Let then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
pragma Assert (Bindings.Length mod 2 = 0);
|
||||
New_Env : constant Environments.Ptr
|
||||
:= Environments.Alloc (Outer => Env);
|
||||
begin
|
||||
New_Env.Increase_Capacity (Bindings.Length / 2);
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
pragma Assert
|
||||
(Bindings.Element (2 * I - 1).Kind = Kind_Symbol);
|
||||
New_Env.Set (Bindings.Element (2 * I - 1).S,
|
||||
Eval (Bindings.Element (2 * I), New_Env));
|
||||
end loop;
|
||||
return Eval (Ast.L.Element (3), New_Env);
|
||||
end;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- No special form has been found, attempt to apply the
|
||||
-- first element to the rest of the list.
|
||||
declare
|
||||
Args : Mal_Type_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
if First.Kind /= Kind_Symbol then
|
||||
-- Evaluate First, in the less frequent case where it is
|
||||
-- not a symbol.
|
||||
First := Eval (First, Env);
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
case First.Kind is
|
||||
when Kind_Native =>
|
||||
return First.Native.all (Args);
|
||||
when others =>
|
||||
raise Unable_To_Call
|
||||
with Ada.Strings.Unbounded.To_String (Print (First));
|
||||
end case;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Def then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "def!: expects 2 arguments";
|
||||
elsif Ast.L.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "def!: arg 1 must be a symbol";
|
||||
end if;
|
||||
return R : constant Mal.T := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).Symbol, R);
|
||||
end return;
|
||||
elsif First.Symbol = Symbols.Names.Let then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "let*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "let*: expects a list or vector";
|
||||
end if;
|
||||
declare
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
New_Env : constant Environments.Ptr := Env.Sub;
|
||||
begin
|
||||
if Bindings.Length mod 2 /= 0 then
|
||||
raise Argument_Error with "let*: odd number of bindings";
|
||||
end if;
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
if Bindings.Element (2 * I - 1).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "let*: keys must be symbols";
|
||||
end if;
|
||||
New_Env.Set (Bindings.Element (2 * I - 1).Symbol,
|
||||
Eval (Bindings.Element (2 * I), New_Env));
|
||||
end loop;
|
||||
return Eval (Ast.L.Element (3), New_Env);
|
||||
end;
|
||||
else
|
||||
-- Equivalent to First := Eval (First, Env), except that
|
||||
-- we already know enough to spare a recursive call in
|
||||
-- this frequent case.
|
||||
First := Env.Get (First.Symbol);
|
||||
end if;
|
||||
-- Apply phase.
|
||||
case First.Kind is
|
||||
when Kind_Builtin =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
return First.Builtin.all (Args);
|
||||
end;
|
||||
when others =>
|
||||
raise Argument_Error
|
||||
with "cannot execute " & ASU.To_String (Print (First));
|
||||
end case;
|
||||
when others =>
|
||||
return Ast;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
function Generic_Mal_Operator (Args : in Types.Mal_Type_Array)
|
||||
return Types.Mal_Type
|
||||
is (Types.Kind_Number, Atoms.No_Element,
|
||||
Ada_Operator (Args (Args'First).Integer_Value,
|
||||
Args (Args'First + 1).Integer_Value));
|
||||
function Generic_Mal_Operator (Args : in Mal.T_Array) return Mal.T
|
||||
is (Kind_Number, Ada_Operator (Args (Args'First).Ada_Number,
|
||||
Args (Args'Last).Ada_Number));
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
is
|
||||
|
||||
function Readline (Prompt : in Interfaces.C.char_array)
|
||||
return Interfaces.C.Strings.chars_ptr
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr) is
|
||||
use Interfaces.C, Interfaces.C.Strings;
|
||||
function Readline (Prompt : in char_array) return chars_ptr
|
||||
with Import, Convention => C, External_Name => "readline";
|
||||
|
||||
procedure Add_History (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Add_History (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "add_history";
|
||||
|
||||
procedure Free (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Free (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "free";
|
||||
|
||||
Prompt : constant Interfaces.C.char_array
|
||||
:= Interfaces.C.To_C ("user> ");
|
||||
C_Line : Interfaces.C.Strings.chars_ptr;
|
||||
Prompt : constant char_array := To_C ("user> ");
|
||||
C_Line : chars_ptr;
|
||||
begin
|
||||
loop
|
||||
C_Line := Readline (Prompt);
|
||||
exit when C_Line = Interfaces.C.Strings.Null_Ptr;
|
||||
exit when C_Line = Null_Ptr;
|
||||
declare
|
||||
Line : constant String := Interfaces.C.Strings.Value (C_Line);
|
||||
Line : constant String := Value (C_Line);
|
||||
begin
|
||||
if Line /= "" then
|
||||
Add_History (C_Line);
|
||||
@ -167,9 +153,10 @@ procedure Step3_Env is
|
||||
exception
|
||||
when Reader.Empty_Source =>
|
||||
null;
|
||||
when E : others =>
|
||||
when E : Argument_Error | Reader.Reader_Error
|
||||
| Environments.Unknown_Key =>
|
||||
Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
|
||||
-- but go on proceeding.
|
||||
-- Other exceptions are unexpected.
|
||||
end;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
@ -182,18 +169,14 @@ procedure Step3_Env is
|
||||
function Product is new Generic_Mal_Operator ("*");
|
||||
function Division is new Generic_Mal_Operator ("/");
|
||||
|
||||
use Types;
|
||||
Repl : constant Environments.Ptr := Environments.Alloc;
|
||||
function S (Source : in String) return Symbols.Ptr
|
||||
renames Symbols.Constructor;
|
||||
Repl : Environments.Ptr renames Environments.Repl;
|
||||
begin
|
||||
Repl.Increase_Capacity (4);
|
||||
Repl.Set (Names.Plus, Types.Mal_Type'
|
||||
(Types.Kind_Native, Atoms.No_Element, Addition'Unrestricted_Access));
|
||||
Repl.Set (Names.Minus, Types.Mal_Type'
|
||||
(Types.Kind_Native, Atoms.No_Element, Subtraction'Unrestricted_Access));
|
||||
Repl.Set (Names.Asterisk, Types.Mal_Type'
|
||||
(Types.Kind_Native, Atoms.No_Element, Product'Unrestricted_Access));
|
||||
Repl.Set (Names.Slash, Types.Mal_Type'
|
||||
(Types.Kind_Native, Atoms.No_Element, Division'Unrestricted_Access));
|
||||
Repl.Set (S ("+"), (Kind_Builtin, Addition 'Unrestricted_Access));
|
||||
Repl.Set (S ("-"), (Kind_Builtin, Subtraction'Unrestricted_Access));
|
||||
Repl.Set (S ("*"), (Kind_Builtin, Product 'Unrestricted_Access));
|
||||
Repl.Set (S ("/"), (Kind_Builtin, Division 'Unrestricted_Access));
|
||||
|
||||
Interactive_Loop (Repl);
|
||||
end Step3_Env;
|
||||
|
@ -1,208 +1,197 @@
|
||||
with Ada.Exceptions;
|
||||
with Ada.Strings.Unbounded;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
with Interfaces.C.Strings; use type Interfaces.C.Strings.chars_ptr;
|
||||
with Atoms;
|
||||
with Interfaces.C.Strings;
|
||||
|
||||
with Core;
|
||||
with Environments;
|
||||
with Lists;
|
||||
with Names;
|
||||
with Printer;
|
||||
with Reader;
|
||||
with Strings; use type Strings.Ptr;
|
||||
with Types;
|
||||
with Types.Functions;
|
||||
with Types.Lists;
|
||||
with Types.Mal;
|
||||
with Types.Maps;
|
||||
with Types.Symbols.Names;
|
||||
|
||||
procedure Step4_If_Fn_Do is
|
||||
|
||||
function Read (Source : in String) return Types.Mal_Type
|
||||
package ASU renames Ada.Strings.Unbounded;
|
||||
use Types;
|
||||
use type Symbols.Ptr;
|
||||
|
||||
function Read (Source : in String) return Mal.T
|
||||
renames Reader.Read_Str;
|
||||
|
||||
function Eval (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
Unable_To_Call : exception;
|
||||
function Eval (Ast : in Mal.T;
|
||||
Env : in Environments.Ptr) return Mal.T;
|
||||
|
||||
function Print (Ast : in Types.Mal_Type;
|
||||
Print_Readably : in Boolean := True)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
function Print (Ast : in Mal.T;
|
||||
Readably : in Boolean := True) return ASU.Unbounded_String
|
||||
renames Printer.Pr_Str;
|
||||
|
||||
function Rep (Source : in String;
|
||||
Env : in Environments.Ptr)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env)))
|
||||
with Inline;
|
||||
Env : in Environments.Ptr) return ASU.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env))) with Inline;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
with Inline;
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr);
|
||||
|
||||
function Eval_Elements is new Lists.Generic_Eval (Environments.Ptr, Eval);
|
||||
function Eval_Elements is new Maps.Generic_Eval (Environments.Ptr, Eval);
|
||||
|
||||
-- Convenient when the result of eval is of no interest.
|
||||
procedure Discard (Ast : in Types.Mal_Type) is null;
|
||||
procedure Discard (Ast : in Mal.T) is null;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function Eval (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type
|
||||
is
|
||||
use Types;
|
||||
First : Mal_Type;
|
||||
function Eval (Ast : in Mal.T;
|
||||
Env : in Environments.Ptr) return Mal.T is
|
||||
First : Mal.T;
|
||||
begin
|
||||
-- Ada.Text_IO.New_Line;
|
||||
-- Ada.Text_IO.Put ("EVAL: ");
|
||||
-- Ada.Text_IO.Unbounded_IO.Put_Line (Print (Ast));
|
||||
-- Environments.Dump_Stack;
|
||||
case Ast.Kind is
|
||||
when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Kind_String
|
||||
| Kind_Keyword | Kind_Macro | Kind_Function | Kind_Native =>
|
||||
return Ast;
|
||||
|
||||
when Kind_Symbol =>
|
||||
return Env.Get (Ast.S);
|
||||
|
||||
return Env.Get (Ast.Symbol);
|
||||
when Kind_Map =>
|
||||
declare
|
||||
function F (X : Mal_Type) return Mal_Type is (Eval (X, Env));
|
||||
begin
|
||||
return (Kind_Map, Atoms.No_Element, Ast.Map.Map (F'Access));
|
||||
end;
|
||||
|
||||
return Eval_Elements (Ast.Map, Env);
|
||||
when Kind_Vector =>
|
||||
return R : constant Mal_Type := (Kind_Vector, Atoms.No_Element,
|
||||
Lists.Alloc (Ast.L.Length))
|
||||
do
|
||||
for I in 1 .. Ast.L.Length loop
|
||||
R.L.Replace_Element (I, Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
end return;
|
||||
|
||||
return (Kind_Vector, Eval_Elements (Ast.L, Env));
|
||||
when Kind_List =>
|
||||
if Ast.L.Length = 0 then
|
||||
return Ast;
|
||||
end if;
|
||||
|
||||
First := Ast.L.Element (1);
|
||||
|
||||
-- Special forms
|
||||
if First.Kind = Kind_Symbol then
|
||||
|
||||
if First.S = Names.Def then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (2).Kind = Kind_Symbol);
|
||||
return R : constant Mal_Type := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).S, R);
|
||||
end return;
|
||||
|
||||
elsif First.S = Names.Mal_Do then
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
return Eval (Ast.L.Element (Ast.L.Length), Env);
|
||||
|
||||
elsif First.S = Names.Fn then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (I).Kind = Kind_Symbol);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).L.Length < 1
|
||||
or else Names.Ampersand /=
|
||||
Ast.L.Element (2).L.Element (Ast.L.Element (2).L.Length).S);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length - 2 =>
|
||||
Ast.L.Element (2).L.Element (I).S /= Names.Ampersand);
|
||||
return (Kind => Kind_Function,
|
||||
Meta => Atoms.No_Element,
|
||||
Formals => Ast.L.Element (2).L,
|
||||
Expression => Atoms.Alloc (Ast.L.Element (3)),
|
||||
Environment => Env);
|
||||
|
||||
elsif First.S = Names.Mal_If then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length in 3 .. 4);
|
||||
Test : constant Mal_Type := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Boolean_Value,
|
||||
when others => True)
|
||||
then
|
||||
return Eval (Ast.L.Element (3), Env);
|
||||
elsif Ast.L.Length = 3 then
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
else
|
||||
return Eval (Ast.L.Element (4), Env);
|
||||
end if;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Let then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
pragma Assert (Bindings.Length mod 2 = 0);
|
||||
New_Env : constant Environments.Ptr
|
||||
:= Environments.Alloc (Outer => Env);
|
||||
begin
|
||||
New_Env.Increase_Capacity (Bindings.Length / 2);
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
pragma Assert
|
||||
(Bindings.Element (2 * I - 1).Kind = Kind_Symbol);
|
||||
New_Env.Set (Bindings.Element (2 * I - 1).S,
|
||||
Eval (Bindings.Element (2 * I), New_Env));
|
||||
end loop;
|
||||
return Eval (Ast.L.Element (3), New_Env);
|
||||
end;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- No special form has been found, attempt to apply the
|
||||
-- first element to the rest of the list.
|
||||
declare
|
||||
Args : Mal_Type_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
if First.Kind /= Kind_Symbol then
|
||||
-- Evaluate First, in the less frequent case where it is
|
||||
-- not a symbol.
|
||||
First := Eval (First, Env);
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
elsif First.Symbol = Symbols.Names.Def then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "def!: expects 2 arguments";
|
||||
elsif Ast.L.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "def!: arg 1 must be a symbol";
|
||||
end if;
|
||||
return R : constant Mal.T := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).Symbol, R);
|
||||
end return;
|
||||
elsif First.Symbol = Symbols.Names.Mal_Do then
|
||||
if Ast.L.Length = 1 then
|
||||
raise Argument_Error with "do: expects at least 1 argument";
|
||||
end if;
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
case First.Kind is
|
||||
when Kind_Native =>
|
||||
return First.Native.all (Args);
|
||||
when Kind_Function =>
|
||||
declare
|
||||
New_Env : constant Environments.Ptr
|
||||
:= Environments.Alloc (Outer => First.Environment);
|
||||
begin
|
||||
New_Env.Set_Binds (First.Formals, Args);
|
||||
return Eval (First.Expression.Deref, New_Env);
|
||||
end;
|
||||
when others =>
|
||||
raise Unable_To_Call
|
||||
with Ada.Strings.Unbounded.To_String (Print (First));
|
||||
end case;
|
||||
end;
|
||||
return Eval (Ast.L.Element (Ast.L.Length), Env);
|
||||
elsif First.Symbol = Symbols.Names.Fn then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "fn*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "fn*: arg 1 must be a list or vector";
|
||||
elsif (for some F in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (F).Kind /= Kind_Symbol)
|
||||
then
|
||||
raise Argument_Error with "fn*: arg 2 must contain symbols";
|
||||
end if;
|
||||
return Functions.New_Function (Ast.L.Element (2).L,
|
||||
Ast.L.Element (3), Env.New_Closure);
|
||||
elsif First.Symbol = Symbols.Names.Mal_If then
|
||||
if Ast.L.Length not in 3 .. 4 then
|
||||
raise Argument_Error with "if: expects 2 or 3 arguments";
|
||||
end if;
|
||||
declare
|
||||
Test : constant Mal.T := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Ada_Boolean,
|
||||
when others => True)
|
||||
then
|
||||
return Eval (Ast.L.Element (3), Env);
|
||||
elsif Ast.L.Length = 3 then
|
||||
return Mal.Nil;
|
||||
else
|
||||
return Eval (Ast.L.Element (4), Env);
|
||||
end if;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Let then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "let*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "let*: expects a list or vector";
|
||||
end if;
|
||||
declare
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
New_Env : constant Environments.Ptr := Env.Sub;
|
||||
begin
|
||||
if Bindings.Length mod 2 /= 0 then
|
||||
raise Argument_Error with "let*: odd number of bindings";
|
||||
end if;
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
if Bindings.Element (2 * I - 1).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "let*: keys must be symbols";
|
||||
end if;
|
||||
New_Env.Set (Bindings.Element (2 * I - 1).Symbol,
|
||||
Eval (Bindings.Element (2 * I), New_Env));
|
||||
end loop;
|
||||
return Eval (Ast.L.Element (3), New_Env);
|
||||
end;
|
||||
else
|
||||
-- Equivalent to First := Eval (First, Env), except that
|
||||
-- we already know enough to spare a recursive call in
|
||||
-- this frequent case.
|
||||
First := Env.Get (First.Symbol);
|
||||
end if;
|
||||
-- Apply phase.
|
||||
case First.Kind is
|
||||
when Kind_Builtin =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
return First.Builtin.all (Args);
|
||||
end;
|
||||
when Kind_Function =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
New_Env : constant Environments.Ptr
|
||||
:= First.Function_Value.Closure.Sub;
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
First.Function_Value.Set_Binds (New_Env, Args);
|
||||
return Eval (First.Function_Value.Expression, New_Env);
|
||||
end;
|
||||
when others =>
|
||||
raise Argument_Error
|
||||
with "cannot execute " & ASU.To_String (Print (First));
|
||||
end case;
|
||||
when others =>
|
||||
return Ast;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
is
|
||||
|
||||
function Readline (Prompt : in Interfaces.C.char_array)
|
||||
return Interfaces.C.Strings.chars_ptr
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr) is
|
||||
use Interfaces.C, Interfaces.C.Strings;
|
||||
function Readline (Prompt : in char_array) return chars_ptr
|
||||
with Import, Convention => C, External_Name => "readline";
|
||||
|
||||
procedure Add_History (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Add_History (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "add_history";
|
||||
|
||||
procedure Free (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Free (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "free";
|
||||
|
||||
Prompt : constant Interfaces.C.char_array
|
||||
:= Interfaces.C.To_C ("user> ");
|
||||
C_Line : Interfaces.C.Strings.chars_ptr;
|
||||
Prompt : constant char_array := To_C ("user> ");
|
||||
C_Line : chars_ptr;
|
||||
begin
|
||||
loop
|
||||
C_Line := Readline (Prompt);
|
||||
exit when C_Line = Interfaces.C.Strings.Null_Ptr;
|
||||
exit when C_Line = Null_Ptr;
|
||||
declare
|
||||
Line : constant String := Interfaces.C.Strings.Value (C_Line);
|
||||
Line : constant String := Value (C_Line);
|
||||
begin
|
||||
if Line /= "" then
|
||||
Add_History (C_Line);
|
||||
@ -212,9 +201,10 @@ procedure Step4_If_Fn_Do is
|
||||
exception
|
||||
when Reader.Empty_Source =>
|
||||
null;
|
||||
when E : others =>
|
||||
when E : Argument_Error | Reader.Reader_Error
|
||||
| Environments.Unknown_Key =>
|
||||
Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
|
||||
-- but go on proceeding.
|
||||
-- Other exceptions are unexpected.
|
||||
end;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
@ -222,10 +212,12 @@ procedure Step4_If_Fn_Do is
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Repl : constant Environments.Ptr := Environments.Alloc;
|
||||
Startup : constant String := "(do"
|
||||
& "(def! not (fn* (a) (if a false true)))"
|
||||
& ")";
|
||||
Repl : Environments.Ptr renames Environments.Repl;
|
||||
begin
|
||||
Core.Add_Built_In_Functions (Repl, Eval'Unrestricted_Access);
|
||||
Discard (Eval (Read ("(def! not (fn* (a) (if a false true)))"), Repl));
|
||||
|
||||
Core.Eval_Ref := Eval'Unrestricted_Access;
|
||||
Discard (Eval (Read (Startup), Repl));
|
||||
Interactive_Loop (Repl);
|
||||
end Step4_If_Fn_Do;
|
||||
|
@ -1,211 +1,206 @@
|
||||
with Ada.Exceptions;
|
||||
with Ada.Strings.Unbounded;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
with Interfaces.C.Strings; use type Interfaces.C.Strings.chars_ptr;
|
||||
with Atoms;
|
||||
with Interfaces.C.Strings;
|
||||
|
||||
with Core;
|
||||
with Environments;
|
||||
with Lists;
|
||||
with Names;
|
||||
with Printer;
|
||||
with Reader;
|
||||
with Strings; use type Strings.Ptr;
|
||||
with Types;
|
||||
with Types.Functions;
|
||||
with Types.Lists;
|
||||
with Types.Mal;
|
||||
with Types.Maps;
|
||||
with Types.Symbols.Names;
|
||||
|
||||
procedure Step5_Tco is
|
||||
|
||||
function Read (Source : in String) return Types.Mal_Type
|
||||
package ASU renames Ada.Strings.Unbounded;
|
||||
use Types;
|
||||
use type Symbols.Ptr;
|
||||
|
||||
function Read (Source : in String) return Mal.T
|
||||
renames Reader.Read_Str;
|
||||
|
||||
function Eval (Rec_Ast : in Types.Mal_Type;
|
||||
Rec_Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
Unable_To_Call : exception;
|
||||
function Eval (Ast0 : in Mal.T;
|
||||
Env0 : in Environments.Ptr) return Mal.T;
|
||||
|
||||
function Print (Ast : in Types.Mal_Type;
|
||||
Print_Readably : in Boolean := True)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
function Print (Ast : in Mal.T;
|
||||
Readably : in Boolean := True) return ASU.Unbounded_String
|
||||
renames Printer.Pr_Str;
|
||||
|
||||
function Rep (Source : in String;
|
||||
Env : in Environments.Ptr)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env)))
|
||||
with Inline;
|
||||
Env : in Environments.Ptr) return ASU.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env))) with Inline;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
with Inline;
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr);
|
||||
|
||||
function Eval_Elements is new Lists.Generic_Eval (Environments.Ptr, Eval);
|
||||
function Eval_Elements is new Maps.Generic_Eval (Environments.Ptr, Eval);
|
||||
|
||||
-- Convenient when the result of eval is of no interest.
|
||||
procedure Discard (Ast : in Types.Mal_Type) is null;
|
||||
procedure Discard (Ast : in Mal.T) is null;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function Eval (Rec_Ast : in Types.Mal_Type;
|
||||
Rec_Env : in Environments.Ptr) return Types.Mal_Type
|
||||
is
|
||||
use Types;
|
||||
Ast : Types.Mal_Type := Rec_Ast;
|
||||
Env : Environments.Ptr := Rec_Env;
|
||||
First : Mal_Type;
|
||||
function Eval (Ast0 : in Mal.T;
|
||||
Env0 : in Environments.Ptr) return Mal.T is
|
||||
-- Use local variables, that can be rewritten when tail call
|
||||
-- optimization goes to <<Restart>>.
|
||||
Ast : Mal.T := Ast0;
|
||||
Env : Environments.Ptr := Env0.Copy_Pointer;
|
||||
First : Mal.T;
|
||||
begin
|
||||
<<Restart>>
|
||||
<<Restart>>
|
||||
-- Ada.Text_IO.New_Line;
|
||||
-- Ada.Text_IO.Put ("EVAL: ");
|
||||
-- Ada.Text_IO.Unbounded_IO.Put_Line (Print (Ast));
|
||||
-- Environments.Dump_Stack;
|
||||
case Ast.Kind is
|
||||
when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Kind_String
|
||||
| Kind_Keyword | Kind_Macro | Kind_Function | Kind_Native =>
|
||||
return Ast;
|
||||
|
||||
when Kind_Symbol =>
|
||||
return Env.Get (Ast.S);
|
||||
|
||||
return Env.Get (Ast.Symbol);
|
||||
when Kind_Map =>
|
||||
declare
|
||||
function F (X : Mal_Type) return Mal_Type is (Eval (X, Env));
|
||||
begin
|
||||
return (Kind_Map, Atoms.No_Element, Ast.Map.Map (F'Access));
|
||||
end;
|
||||
|
||||
return Eval_Elements (Ast.Map, Env);
|
||||
when Kind_Vector =>
|
||||
return R : constant Mal_Type := (Kind_Vector, Atoms.No_Element,
|
||||
Lists.Alloc (Ast.L.Length))
|
||||
do
|
||||
for I in 1 .. Ast.L.Length loop
|
||||
R.L.Replace_Element (I, Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
end return;
|
||||
|
||||
return (Kind_Vector, Eval_Elements (Ast.L, Env));
|
||||
when Kind_List =>
|
||||
if Ast.L.Length = 0 then
|
||||
return Ast;
|
||||
end if;
|
||||
|
||||
First := Ast.L.Element (1);
|
||||
|
||||
-- Special forms
|
||||
if First.Kind = Kind_Symbol then
|
||||
|
||||
if First.S = Names.Def then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (2).Kind = Kind_Symbol);
|
||||
return R : constant Mal_Type := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).S, R);
|
||||
end return;
|
||||
|
||||
elsif First.S = Names.Mal_Do then
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (Ast.L.Length);
|
||||
goto Restart;
|
||||
|
||||
elsif First.S = Names.Fn then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (I).Kind = Kind_Symbol);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).L.Length < 1
|
||||
or else Names.Ampersand /=
|
||||
Ast.L.Element (2).L.Element (Ast.L.Element (2).L.Length).S);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length - 2 =>
|
||||
Ast.L.Element (2).L.Element (I).S /= Names.Ampersand);
|
||||
return (Kind => Kind_Function,
|
||||
Meta => Atoms.No_Element,
|
||||
Formals => Ast.L.Element (2).L,
|
||||
Expression => Atoms.Alloc (Ast.L.Element (3)),
|
||||
Environment => Env);
|
||||
|
||||
elsif First.S = Names.Mal_If then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length in 3 .. 4);
|
||||
Test : constant Mal_Type := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Boolean_Value,
|
||||
when others => True)
|
||||
then
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
elsif Ast.L.Length = 3 then
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
else
|
||||
Ast := Ast.L.Element (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Let then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
pragma Assert (Bindings.Length mod 2 = 0);
|
||||
begin
|
||||
Env.Replace_With_Subenv;
|
||||
Env.Increase_Capacity (Bindings.Length / 2);
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
pragma Assert
|
||||
(Bindings.Element (2 * I - 1).Kind = Kind_Symbol);
|
||||
Env.Set (Bindings.Element (2 * I - 1).S,
|
||||
Eval (Bindings.Element (2 * I), Env));
|
||||
end loop;
|
||||
if First.Kind /= Kind_Symbol then
|
||||
-- Evaluate First, in the less frequent case where it is
|
||||
-- not a symbol.
|
||||
First := Eval (First, Env);
|
||||
elsif First.Symbol = Symbols.Names.Def then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "def!: expects 2 arguments";
|
||||
elsif Ast.L.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "def!: arg 1 must be a symbol";
|
||||
end if;
|
||||
return R : constant Mal.T := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).Symbol, R);
|
||||
end return;
|
||||
elsif First.Symbol = Symbols.Names.Mal_Do then
|
||||
if Ast.L.Length = 1 then
|
||||
raise Argument_Error with "do: expects at least 1 argument";
|
||||
end if;
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (Ast.L.Length);
|
||||
goto Restart;
|
||||
elsif First.Symbol = Symbols.Names.Fn then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "fn*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "fn*: arg 1 must be a list or vector";
|
||||
elsif (for some F in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (F).Kind /= Kind_Symbol)
|
||||
then
|
||||
raise Argument_Error with "fn*: arg 2 must contain symbols";
|
||||
end if;
|
||||
return Functions.New_Function (Ast.L.Element (2).L,
|
||||
Ast.L.Element (3), Env.New_Closure);
|
||||
elsif First.Symbol = Symbols.Names.Mal_If then
|
||||
if Ast.L.Length not in 3 .. 4 then
|
||||
raise Argument_Error with "if: expects 2 or 3 arguments";
|
||||
end if;
|
||||
declare
|
||||
Test : constant Mal.T := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Ada_Boolean,
|
||||
when others => True)
|
||||
then
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
end;
|
||||
elsif Ast.L.Length = 3 then
|
||||
return Mal.Nil;
|
||||
else
|
||||
Ast := Ast.L.Element (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Let then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "let*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "let*: expects a list or vector";
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- No special form has been found, attempt to apply the
|
||||
-- first element to the rest of the list.
|
||||
declare
|
||||
Args : Mal_Type_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
First := Eval (First, Env);
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
case First.Kind is
|
||||
when Kind_Native =>
|
||||
return First.Native.all (Args);
|
||||
when Kind_Function =>
|
||||
Env := Environments.Alloc (Outer => First.Environment);
|
||||
Env.Set_Binds (First.Formals, Args);
|
||||
Ast := First.Expression.Deref;
|
||||
declare
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
begin
|
||||
if Bindings.Length mod 2 /= 0 then
|
||||
raise Argument_Error with "let*: odd number of bindings";
|
||||
end if;
|
||||
Env.Replace_With_Sub;
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
if Bindings.Element (2 * I - 1).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "let*: keys must be symbols";
|
||||
end if;
|
||||
Env.Set (Bindings.Element (2 * I - 1).Symbol,
|
||||
Eval (Bindings.Element (2 * I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
when others =>
|
||||
raise Unable_To_Call
|
||||
with Ada.Strings.Unbounded.To_String (Print (First));
|
||||
end case;
|
||||
end;
|
||||
end;
|
||||
else
|
||||
-- Equivalent to First := Eval (First, Env), except that
|
||||
-- we already know enough to spare a recursive call in
|
||||
-- this frequent case.
|
||||
First := Env.Get (First.Symbol);
|
||||
end if;
|
||||
-- Apply phase.
|
||||
case First.Kind is
|
||||
when Kind_Builtin =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
return First.Builtin.all (Args);
|
||||
end;
|
||||
when Kind_Function =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
Env.Replace_With_Sub (First.Function_Value.Closure);
|
||||
First.Function_Value.Set_Binds (Env, Args);
|
||||
Ast := First.Function_Value.Expression;
|
||||
goto Restart;
|
||||
end;
|
||||
when others =>
|
||||
raise Argument_Error
|
||||
with "cannot execute " & ASU.To_String (Print (First));
|
||||
end case;
|
||||
when others =>
|
||||
return Ast;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
is
|
||||
|
||||
function Readline (Prompt : in Interfaces.C.char_array)
|
||||
return Interfaces.C.Strings.chars_ptr
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr) is
|
||||
use Interfaces.C, Interfaces.C.Strings;
|
||||
function Readline (Prompt : in char_array) return chars_ptr
|
||||
with Import, Convention => C, External_Name => "readline";
|
||||
|
||||
procedure Add_History (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Add_History (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "add_history";
|
||||
|
||||
procedure Free (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Free (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "free";
|
||||
|
||||
Prompt : constant Interfaces.C.char_array
|
||||
:= Interfaces.C.To_C ("user> ");
|
||||
C_Line : Interfaces.C.Strings.chars_ptr;
|
||||
Prompt : constant char_array := To_C ("user> ");
|
||||
C_Line : chars_ptr;
|
||||
begin
|
||||
loop
|
||||
C_Line := Readline (Prompt);
|
||||
exit when C_Line = Interfaces.C.Strings.Null_Ptr;
|
||||
exit when C_Line = Null_Ptr;
|
||||
declare
|
||||
Line : constant String := Interfaces.C.Strings.Value (C_Line);
|
||||
Line : constant String := Value (C_Line);
|
||||
begin
|
||||
if Line /= "" then
|
||||
Add_History (C_Line);
|
||||
@ -215,9 +210,10 @@ procedure Step5_Tco is
|
||||
exception
|
||||
when Reader.Empty_Source =>
|
||||
null;
|
||||
when E : others =>
|
||||
when E : Argument_Error | Reader.Reader_Error
|
||||
| Environments.Unknown_Key =>
|
||||
Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
|
||||
-- but go on proceeding.
|
||||
-- Other exceptions are unexpected.
|
||||
end;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
@ -225,10 +221,12 @@ procedure Step5_Tco is
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Repl : constant Environments.Ptr := Environments.Alloc;
|
||||
Startup : constant String := "(do"
|
||||
& "(def! not (fn* (a) (if a false true)))"
|
||||
& ")";
|
||||
Repl : Environments.Ptr renames Environments.Repl;
|
||||
begin
|
||||
Core.Add_Built_In_Functions (Repl, Eval'Unrestricted_Access);
|
||||
Discard (Eval (Read ("(def! not (fn* (a) (if a false true)))"), Repl));
|
||||
|
||||
Core.Eval_Ref := Eval'Unrestricted_Access;
|
||||
Discard (Eval (Read (Startup), Repl));
|
||||
Interactive_Loop (Repl);
|
||||
end Step5_Tco;
|
||||
|
@ -2,214 +2,206 @@ with Ada.Command_Line;
|
||||
with Ada.Exceptions;
|
||||
with Ada.Strings.Unbounded;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
with Interfaces.C.Strings; use type Interfaces.C.Strings.chars_ptr;
|
||||
with Atoms;
|
||||
with Interfaces.C.Strings;
|
||||
|
||||
with Core;
|
||||
with Environments;
|
||||
with Lists;
|
||||
with Names;
|
||||
with Printer;
|
||||
with Reader;
|
||||
with Strings; use type Strings.Ptr;
|
||||
with Types;
|
||||
with Types.Functions;
|
||||
with Types.Lists;
|
||||
with Types.Mal;
|
||||
with Types.Maps;
|
||||
with Types.Symbols.Names;
|
||||
|
||||
procedure Step6_File is
|
||||
|
||||
function Read (Source : in String) return Types.Mal_Type
|
||||
package ASU renames Ada.Strings.Unbounded;
|
||||
use Types;
|
||||
use type Symbols.Ptr;
|
||||
|
||||
function Read (Source : in String) return Mal.T
|
||||
renames Reader.Read_Str;
|
||||
|
||||
function Eval (Rec_Ast : in Types.Mal_Type;
|
||||
Rec_Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
Unable_To_Call : exception;
|
||||
function Eval (Ast0 : in Mal.T;
|
||||
Env0 : in Environments.Ptr) return Mal.T;
|
||||
|
||||
function Print (Ast : in Types.Mal_Type;
|
||||
Print_Readably : in Boolean := True)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
function Print (Ast : in Mal.T;
|
||||
Readably : in Boolean := True) return ASU.Unbounded_String
|
||||
renames Printer.Pr_Str;
|
||||
|
||||
function Rep (Source : in String;
|
||||
Env : in Environments.Ptr)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env)))
|
||||
with Inline;
|
||||
Env : in Environments.Ptr) return ASU.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env))) with Inline;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
with Inline;
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr);
|
||||
|
||||
function Eval_Elements is new Lists.Generic_Eval (Environments.Ptr, Eval);
|
||||
function Eval_Elements is new Maps.Generic_Eval (Environments.Ptr, Eval);
|
||||
|
||||
-- Convenient when the result of eval is of no interest.
|
||||
procedure Discard (Ast : in Types.Mal_Type) is null;
|
||||
|
||||
-- Eval, with a profile compatible with Native_Function_Access.
|
||||
function Eval_Native (Args : in Types.Mal_Type_Array) return Types.Mal_Type;
|
||||
procedure Discard (Ast : in Mal.T) is null;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function Eval (Rec_Ast : in Types.Mal_Type;
|
||||
Rec_Env : in Environments.Ptr) return Types.Mal_Type
|
||||
is
|
||||
use Types;
|
||||
Ast : Types.Mal_Type := Rec_Ast;
|
||||
Env : Environments.Ptr := Rec_Env;
|
||||
First : Mal_Type;
|
||||
function Eval (Ast0 : in Mal.T;
|
||||
Env0 : in Environments.Ptr) return Mal.T is
|
||||
-- Use local variables, that can be rewritten when tail call
|
||||
-- optimization goes to <<Restart>>.
|
||||
Ast : Mal.T := Ast0;
|
||||
Env : Environments.Ptr := Env0.Copy_Pointer;
|
||||
First : Mal.T;
|
||||
begin
|
||||
<<Restart>>
|
||||
<<Restart>>
|
||||
-- Ada.Text_IO.New_Line;
|
||||
-- Ada.Text_IO.Put ("EVAL: ");
|
||||
-- Ada.Text_IO.Unbounded_IO.Put_Line (Print (Ast));
|
||||
-- Environments.Dump_Stack;
|
||||
case Ast.Kind is
|
||||
when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Kind_String
|
||||
| Kind_Keyword | Kind_Macro | Kind_Function | Kind_Native =>
|
||||
return Ast;
|
||||
|
||||
when Kind_Symbol =>
|
||||
return Env.Get (Ast.S);
|
||||
|
||||
return Env.Get (Ast.Symbol);
|
||||
when Kind_Map =>
|
||||
declare
|
||||
function F (X : Mal_Type) return Mal_Type is (Eval (X, Env));
|
||||
begin
|
||||
return (Kind_Map, Atoms.No_Element, Ast.Map.Map (F'Access));
|
||||
end;
|
||||
|
||||
return Eval_Elements (Ast.Map, Env);
|
||||
when Kind_Vector =>
|
||||
return R : constant Mal_Type := (Kind_Vector, Atoms.No_Element,
|
||||
Lists.Alloc (Ast.L.Length))
|
||||
do
|
||||
for I in 1 .. Ast.L.Length loop
|
||||
R.L.Replace_Element (I, Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
end return;
|
||||
|
||||
return (Kind_Vector, Eval_Elements (Ast.L, Env));
|
||||
when Kind_List =>
|
||||
if Ast.L.Length = 0 then
|
||||
return Ast;
|
||||
end if;
|
||||
|
||||
First := Ast.L.Element (1);
|
||||
|
||||
-- Special forms
|
||||
if First.Kind = Kind_Symbol then
|
||||
|
||||
if First.S = Names.Def then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (2).Kind = Kind_Symbol);
|
||||
return R : constant Mal_Type := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).S, R);
|
||||
end return;
|
||||
|
||||
elsif First.S = Names.Mal_Do then
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (Ast.L.Length);
|
||||
goto Restart;
|
||||
|
||||
elsif First.S = Names.Fn then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (I).Kind = Kind_Symbol);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).L.Length < 1
|
||||
or else Names.Ampersand /=
|
||||
Ast.L.Element (2).L.Element (Ast.L.Element (2).L.Length).S);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length - 2 =>
|
||||
Ast.L.Element (2).L.Element (I).S /= Names.Ampersand);
|
||||
return (Kind => Kind_Function,
|
||||
Meta => Atoms.No_Element,
|
||||
Formals => Ast.L.Element (2).L,
|
||||
Expression => Atoms.Alloc (Ast.L.Element (3)),
|
||||
Environment => Env);
|
||||
|
||||
elsif First.S = Names.Mal_If then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length in 3 .. 4);
|
||||
Test : constant Mal_Type := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Boolean_Value,
|
||||
when others => True)
|
||||
then
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
elsif Ast.L.Length = 3 then
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
else
|
||||
Ast := Ast.L.Element (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Let then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
pragma Assert (Bindings.Length mod 2 = 0);
|
||||
begin
|
||||
Env.Replace_With_Subenv;
|
||||
Env.Increase_Capacity (Bindings.Length / 2);
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
pragma Assert
|
||||
(Bindings.Element (2 * I - 1).Kind = Kind_Symbol);
|
||||
Env.Set (Bindings.Element (2 * I - 1).S,
|
||||
Eval (Bindings.Element (2 * I), Env));
|
||||
end loop;
|
||||
if First.Kind /= Kind_Symbol then
|
||||
-- Evaluate First, in the less frequent case where it is
|
||||
-- not a symbol.
|
||||
First := Eval (First, Env);
|
||||
elsif First.Symbol = Symbols.Names.Def then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "def!: expects 2 arguments";
|
||||
elsif Ast.L.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "def!: arg 1 must be a symbol";
|
||||
end if;
|
||||
return R : constant Mal.T := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).Symbol, R);
|
||||
end return;
|
||||
elsif First.Symbol = Symbols.Names.Mal_Do then
|
||||
if Ast.L.Length = 1 then
|
||||
raise Argument_Error with "do: expects at least 1 argument";
|
||||
end if;
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (Ast.L.Length);
|
||||
goto Restart;
|
||||
elsif First.Symbol = Symbols.Names.Fn then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "fn*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "fn*: arg 1 must be a list or vector";
|
||||
elsif (for some F in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (F).Kind /= Kind_Symbol)
|
||||
then
|
||||
raise Argument_Error with "fn*: arg 2 must contain symbols";
|
||||
end if;
|
||||
return Functions.New_Function (Ast.L.Element (2).L,
|
||||
Ast.L.Element (3), Env.New_Closure);
|
||||
elsif First.Symbol = Symbols.Names.Mal_If then
|
||||
if Ast.L.Length not in 3 .. 4 then
|
||||
raise Argument_Error with "if: expects 2 or 3 arguments";
|
||||
end if;
|
||||
declare
|
||||
Test : constant Mal.T := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Ada_Boolean,
|
||||
when others => True)
|
||||
then
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
end;
|
||||
elsif Ast.L.Length = 3 then
|
||||
return Mal.Nil;
|
||||
else
|
||||
Ast := Ast.L.Element (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Let then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "let*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "let*: expects a list or vector";
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- No special form has been found, attempt to apply the
|
||||
-- first element to the rest of the list.
|
||||
declare
|
||||
Args : Mal_Type_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
First := Eval (First, Env);
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
case First.Kind is
|
||||
when Kind_Native =>
|
||||
return First.Native.all (Args);
|
||||
when Kind_Function =>
|
||||
Env := Environments.Alloc (Outer => First.Environment);
|
||||
Env.Set_Binds (First.Formals, Args);
|
||||
Ast := First.Expression.Deref;
|
||||
declare
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
begin
|
||||
if Bindings.Length mod 2 /= 0 then
|
||||
raise Argument_Error with "let*: odd number of bindings";
|
||||
end if;
|
||||
Env.Replace_With_Sub;
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
if Bindings.Element (2 * I - 1).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "let*: keys must be symbols";
|
||||
end if;
|
||||
Env.Set (Bindings.Element (2 * I - 1).Symbol,
|
||||
Eval (Bindings.Element (2 * I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
when others =>
|
||||
raise Unable_To_Call
|
||||
with Ada.Strings.Unbounded.To_String (Print (First));
|
||||
end case;
|
||||
end;
|
||||
end;
|
||||
else
|
||||
-- Equivalent to First := Eval (First, Env), except that
|
||||
-- we already know enough to spare a recursive call in
|
||||
-- this frequent case.
|
||||
First := Env.Get (First.Symbol);
|
||||
end if;
|
||||
-- Apply phase.
|
||||
case First.Kind is
|
||||
when Kind_Builtin =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
return First.Builtin.all (Args);
|
||||
end;
|
||||
when Kind_Function =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
Env.Replace_With_Sub (First.Function_Value.Closure);
|
||||
First.Function_Value.Set_Binds (Env, Args);
|
||||
Ast := First.Function_Value.Expression;
|
||||
goto Restart;
|
||||
end;
|
||||
when others =>
|
||||
raise Argument_Error
|
||||
with "cannot execute " & ASU.To_String (Print (First));
|
||||
end case;
|
||||
when others =>
|
||||
return Ast;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
is
|
||||
|
||||
function Readline (Prompt : in Interfaces.C.char_array)
|
||||
return Interfaces.C.Strings.chars_ptr
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr) is
|
||||
use Interfaces.C, Interfaces.C.Strings;
|
||||
function Readline (Prompt : in char_array) return chars_ptr
|
||||
with Import, Convention => C, External_Name => "readline";
|
||||
|
||||
procedure Add_History (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Add_History (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "add_history";
|
||||
|
||||
procedure Free (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Free (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "free";
|
||||
|
||||
Prompt : constant Interfaces.C.char_array
|
||||
:= Interfaces.C.To_C ("user> ");
|
||||
C_Line : Interfaces.C.Strings.chars_ptr;
|
||||
Prompt : constant char_array := To_C ("user> ");
|
||||
C_Line : chars_ptr;
|
||||
begin
|
||||
loop
|
||||
C_Line := Readline (Prompt);
|
||||
exit when C_Line = Interfaces.C.Strings.Null_Ptr;
|
||||
exit when C_Line = Null_Ptr;
|
||||
declare
|
||||
Line : constant String := Interfaces.C.Strings.Value (C_Line);
|
||||
Line : constant String := Value (C_Line);
|
||||
begin
|
||||
if Line /= "" then
|
||||
Add_History (C_Line);
|
||||
@ -219,9 +211,10 @@ procedure Step6_File is
|
||||
exception
|
||||
when Reader.Empty_Source =>
|
||||
null;
|
||||
when E : others =>
|
||||
when E : Argument_Error | Reader.Reader_Error
|
||||
| Environments.Unknown_Key =>
|
||||
Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
|
||||
-- but go on proceeding.
|
||||
-- Other exceptions are unexpected.
|
||||
end;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
@ -229,32 +222,27 @@ procedure Step6_File is
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
use Types;
|
||||
Argv : Mal_Type (Kind_List);
|
||||
Repl : constant Environments.Ptr := Environments.Alloc;
|
||||
function Eval_Native (Args : in Mal_Type_Array) return Mal_Type is
|
||||
(Eval (Args (Args'First), Repl));
|
||||
Startup : constant String := "(do"
|
||||
& "(def! not (fn* (a) (if a false true)))"
|
||||
& "(def! load-file (fn* (f)"
|
||||
& " (eval (read-string (str ""(do "" (slurp f) "")"")))))"
|
||||
& ")";
|
||||
Repl : Environments.Ptr renames Environments.Repl;
|
||||
use Ada.Command_Line;
|
||||
begin
|
||||
Core.Add_Built_In_Functions (Repl, Eval'Unrestricted_Access);
|
||||
Repl.Set (Names.Eval, Mal_Type'(Kind_Native, Atoms.No_Element,
|
||||
Eval_Native'Unrestricted_Access));
|
||||
|
||||
Discard (Eval (Read ("(def! not (fn* (a) (if a false true)))"), Repl));
|
||||
Discard (Eval (Read ("(def! load-file (fn* (f) "
|
||||
& "(eval (read-string (str ""(do "" (slurp f) "")"")))))"), Repl));
|
||||
|
||||
if Ada.Command_Line.Argument_Count = 0 then
|
||||
Repl.Set (Names.Argv, Argv);
|
||||
Core.Eval_Ref := Eval'Unrestricted_Access;
|
||||
Discard (Eval (Read (Startup), Repl));
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Argument_Count);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := (Kind_String, ASU.To_Unbounded_String (Argument (I)));
|
||||
end loop;
|
||||
Repl.Set (Symbols.Constructor ("*ARGV*"), Lists.List (Args));
|
||||
end;
|
||||
if Argument_Count = 0 then
|
||||
Interactive_Loop (Repl);
|
||||
else
|
||||
Argv.L := Lists.Alloc (Ada.Command_Line.Argument_Count - 1);
|
||||
for I in 2 .. Ada.Command_Line.Argument_Count loop
|
||||
Argv.L.Replace_Element (I - 1,
|
||||
Mal_Type'(Kind_String, Atoms.No_Element,
|
||||
Strings.Alloc (Ada.Command_Line.Argument (I))));
|
||||
end loop;
|
||||
Repl.Set (Names.Argv, Argv);
|
||||
Discard (Eval (Read ("(load-file """ & Ada.Command_Line.Argument (1)
|
||||
& """)"), Repl));
|
||||
Discard (Eval (Read ("(load-file """ & Argument (1) & """)"), Repl));
|
||||
end if;
|
||||
end Step6_File;
|
||||
|
@ -1,236 +1,227 @@
|
||||
with Ada.Containers.Vectors;
|
||||
with Ada.Command_Line;
|
||||
with Ada.Exceptions;
|
||||
with Ada.Strings.Unbounded;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
with Interfaces.C.Strings; use type Interfaces.C.Strings.chars_ptr;
|
||||
with Atoms;
|
||||
with Interfaces.C.Strings;
|
||||
|
||||
with Core;
|
||||
with Environments;
|
||||
with Lists;
|
||||
with Names;
|
||||
with Printer;
|
||||
with Reader;
|
||||
with Strings; use type Strings.Ptr;
|
||||
with Types; use type Types.Kind_Type;
|
||||
with Types.Functions;
|
||||
with Types.Lists;
|
||||
with Types.Mal;
|
||||
with Types.Maps;
|
||||
with Types.Symbols.Names;
|
||||
|
||||
procedure Step7_Quote is
|
||||
|
||||
function Read (Source : in String) return Types.Mal_Type
|
||||
package ASU renames Ada.Strings.Unbounded;
|
||||
use Types;
|
||||
use type Symbols.Ptr;
|
||||
|
||||
function Read (Source : in String) return Mal.T
|
||||
renames Reader.Read_Str;
|
||||
|
||||
function Eval (Rec_Ast : in Types.Mal_Type;
|
||||
Rec_Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
Unable_To_Call : exception;
|
||||
function Eval (Ast0 : in Mal.T;
|
||||
Env0 : in Environments.Ptr) return Mal.T;
|
||||
|
||||
function Quasiquote (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
function Quasiquote (Ast : in Mal.T;
|
||||
Env : in Environments.Ptr) return Mal.T;
|
||||
function Quasiquote (List : in Lists.Ptr;
|
||||
Env : in Environments.Ptr) return Lists.Ptr
|
||||
with Inline;
|
||||
Env : in Environments.Ptr) return Mal.T with Inline;
|
||||
-- Handle vectors and lists not starting with unquote.
|
||||
-- Mergeing quote and quasiquote into eval with a flag triggering
|
||||
-- a different behaviour as done for macros in step8 would improve
|
||||
-- the performances significantly, but Kanaka finds that it breaks
|
||||
-- too much the step structure shared by all implementations.
|
||||
|
||||
function Print (Ast : in Types.Mal_Type;
|
||||
Print_Readably : in Boolean := True)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
function Print (Ast : in Mal.T;
|
||||
Readably : in Boolean := True) return ASU.Unbounded_String
|
||||
renames Printer.Pr_Str;
|
||||
|
||||
function Rep (Source : in String;
|
||||
Env : in Environments.Ptr)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env)))
|
||||
with Inline;
|
||||
Env : in Environments.Ptr) return ASU.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env))) with Inline;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
with Inline;
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr);
|
||||
|
||||
function Eval_Elements is new Lists.Generic_Eval (Environments.Ptr, Eval);
|
||||
function Eval_Elements is new Maps.Generic_Eval (Environments.Ptr, Eval);
|
||||
|
||||
-- Convenient when the result of eval is of no interest.
|
||||
procedure Discard (Ast : in Types.Mal_Type) is null;
|
||||
|
||||
-- Eval, with a profile compatible with Native_Function_Access.
|
||||
function Eval_Native (Args : in Types.Mal_Type_Array) return Types.Mal_Type;
|
||||
|
||||
package Mal_Type_Vectors is new Ada.Containers.Vectors
|
||||
(Index_Type => Positive,
|
||||
Element_Type => Types.Mal_Type,
|
||||
"=" => Types."=");
|
||||
procedure Discard (Ast : in Mal.T) is null;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function Eval (Rec_Ast : in Types.Mal_Type;
|
||||
Rec_Env : in Environments.Ptr) return Types.Mal_Type
|
||||
is
|
||||
use Types;
|
||||
Ast : Types.Mal_Type := Rec_Ast;
|
||||
Env : Environments.Ptr := Rec_Env;
|
||||
First : Mal_Type;
|
||||
function Eval (Ast0 : in Mal.T;
|
||||
Env0 : in Environments.Ptr) return Mal.T is
|
||||
-- Use local variables, that can be rewritten when tail call
|
||||
-- optimization goes to <<Restart>>.
|
||||
Ast : Mal.T := Ast0;
|
||||
Env : Environments.Ptr := Env0.Copy_Pointer;
|
||||
First : Mal.T;
|
||||
begin
|
||||
<<Restart>>
|
||||
<<Restart>>
|
||||
-- Ada.Text_IO.New_Line;
|
||||
-- Ada.Text_IO.Put ("EVAL: ");
|
||||
-- Ada.Text_IO.Unbounded_IO.Put_Line (Print (Ast));
|
||||
-- Environments.Dump_Stack;
|
||||
case Ast.Kind is
|
||||
when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Kind_String
|
||||
| Kind_Keyword | Kind_Macro | Kind_Function | Kind_Native =>
|
||||
return Ast;
|
||||
|
||||
when Kind_Symbol =>
|
||||
return Env.Get (Ast.S);
|
||||
|
||||
return Env.Get (Ast.Symbol);
|
||||
when Kind_Map =>
|
||||
declare
|
||||
function F (X : Mal_Type) return Mal_Type is (Eval (X, Env));
|
||||
begin
|
||||
return (Kind_Map, Atoms.No_Element, Ast.Map.Map (F'Access));
|
||||
end;
|
||||
|
||||
return Eval_Elements (Ast.Map, Env);
|
||||
when Kind_Vector =>
|
||||
return R : constant Mal_Type := (Kind_Vector, Atoms.No_Element,
|
||||
Lists.Alloc (Ast.L.Length))
|
||||
do
|
||||
for I in 1 .. Ast.L.Length loop
|
||||
R.L.Replace_Element (I, Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
end return;
|
||||
|
||||
return (Kind_Vector, Eval_Elements (Ast.L, Env));
|
||||
when Kind_List =>
|
||||
if Ast.L.Length = 0 then
|
||||
return Ast;
|
||||
end if;
|
||||
|
||||
First := Ast.L.Element (1);
|
||||
|
||||
-- Special forms
|
||||
if First.Kind = Kind_Symbol then
|
||||
|
||||
if First.S = Names.Def then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (2).Kind = Kind_Symbol);
|
||||
return R : constant Mal_Type := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).S, R);
|
||||
end return;
|
||||
|
||||
elsif First.S = Names.Mal_Do then
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (Ast.L.Length);
|
||||
goto Restart;
|
||||
|
||||
elsif First.S = Names.Fn then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (I).Kind = Kind_Symbol);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).L.Length < 1
|
||||
or else Names.Ampersand /=
|
||||
Ast.L.Element (2).L.Element (Ast.L.Element (2).L.Length).S);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length - 2 =>
|
||||
Ast.L.Element (2).L.Element (I).S /= Names.Ampersand);
|
||||
return (Kind => Kind_Function,
|
||||
Meta => Atoms.No_Element,
|
||||
Formals => Ast.L.Element (2).L,
|
||||
Expression => Atoms.Alloc (Ast.L.Element (3)),
|
||||
Environment => Env);
|
||||
|
||||
elsif First.S = Names.Mal_If then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length in 3 .. 4);
|
||||
Test : constant Mal_Type := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Boolean_Value,
|
||||
when others => True)
|
||||
then
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
elsif Ast.L.Length = 3 then
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
else
|
||||
Ast := Ast.L.Element (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Let then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
pragma Assert (Bindings.Length mod 2 = 0);
|
||||
begin
|
||||
Env.Replace_With_Subenv;
|
||||
Env.Increase_Capacity (Bindings.Length / 2);
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
pragma Assert
|
||||
(Bindings.Element (2 * I - 1).Kind = Kind_Symbol);
|
||||
Env.Set (Bindings.Element (2 * I - 1).S,
|
||||
Eval (Bindings.Element (2 * I), Env));
|
||||
end loop;
|
||||
if First.Kind /= Kind_Symbol then
|
||||
-- Evaluate First, in the less frequent case where it is
|
||||
-- not a symbol.
|
||||
First := Eval (First, Env);
|
||||
elsif First.Symbol = Symbols.Names.Def then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "def!: expects 2 arguments";
|
||||
elsif Ast.L.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "def!: arg 1 must be a symbol";
|
||||
end if;
|
||||
return R : constant Mal.T := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).Symbol, R);
|
||||
end return;
|
||||
elsif First.Symbol = Symbols.Names.Mal_Do then
|
||||
if Ast.L.Length = 1 then
|
||||
raise Argument_Error with "do: expects at least 1 argument";
|
||||
end if;
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (Ast.L.Length);
|
||||
goto Restart;
|
||||
elsif First.Symbol = Symbols.Names.Fn then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "fn*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "fn*: arg 1 must be a list or vector";
|
||||
elsif (for some F in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (F).Kind /= Kind_Symbol)
|
||||
then
|
||||
raise Argument_Error with "fn*: arg 2 must contain symbols";
|
||||
end if;
|
||||
return Functions.New_Function (Ast.L.Element (2).L,
|
||||
Ast.L.Element (3), Env.New_Closure);
|
||||
elsif First.Symbol = Symbols.Names.Mal_If then
|
||||
if Ast.L.Length not in 3 .. 4 then
|
||||
raise Argument_Error with "if: expects 2 or 3 arguments";
|
||||
end if;
|
||||
declare
|
||||
Test : constant Mal.T := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Ada_Boolean,
|
||||
when others => True)
|
||||
then
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Quote then
|
||||
pragma Assert (Ast.L.Length = 2);
|
||||
return Ast.L.Element (2);
|
||||
|
||||
elsif First.S = Names.Quasiquote then
|
||||
pragma Assert (Ast.L.Length = 2);
|
||||
return Quasiquote (Ast.L.Element (2), Env);
|
||||
elsif Ast.L.Length = 3 then
|
||||
return Mal.Nil;
|
||||
else
|
||||
Ast := Ast.L.Element (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Let then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "let*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "let*: expects a list or vector";
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- No special form has been found, attempt to apply the
|
||||
-- first element to the rest of the list.
|
||||
declare
|
||||
Args : Mal_Type_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
First := Eval (First, Env);
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
case First.Kind is
|
||||
when Kind_Native =>
|
||||
return First.Native.all (Args);
|
||||
when Kind_Function =>
|
||||
Env := Environments.Alloc (Outer => First.Environment);
|
||||
Env.Set_Binds (First.Formals, Args);
|
||||
Ast := First.Expression.Deref;
|
||||
declare
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
begin
|
||||
if Bindings.Length mod 2 /= 0 then
|
||||
raise Argument_Error with "let*: odd number of bindings";
|
||||
end if;
|
||||
Env.Replace_With_Sub;
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
if Bindings.Element (2 * I - 1).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "let*: keys must be symbols";
|
||||
end if;
|
||||
Env.Set (Bindings.Element (2 * I - 1).Symbol,
|
||||
Eval (Bindings.Element (2 * I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
when others =>
|
||||
raise Unable_To_Call
|
||||
with Ada.Strings.Unbounded.To_String (Print (First));
|
||||
end case;
|
||||
end;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Quasiquote then
|
||||
if Ast.L.Length /= 2 then
|
||||
raise Argument_Error with "quasiquote: expects 1 argument";
|
||||
end if;
|
||||
return Quasiquote (Ast.L.Element (2), Env);
|
||||
elsif First.Symbol = Symbols.Names.Quote then
|
||||
if Ast.L.Length /= 2 then
|
||||
raise Argument_Error with "quote: expects 1 argument";
|
||||
end if;
|
||||
return Ast.L.Element (2);
|
||||
else
|
||||
-- Equivalent to First := Eval (First, Env), except that
|
||||
-- we already know enough to spare a recursive call in
|
||||
-- this frequent case.
|
||||
First := Env.Get (First.Symbol);
|
||||
end if;
|
||||
-- Apply phase.
|
||||
case First.Kind is
|
||||
when Kind_Builtin =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
return First.Builtin.all (Args);
|
||||
end;
|
||||
when Kind_Function =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
Env.Replace_With_Sub (First.Function_Value.Closure);
|
||||
First.Function_Value.Set_Binds (Env, Args);
|
||||
Ast := First.Function_Value.Expression;
|
||||
goto Restart;
|
||||
end;
|
||||
when others =>
|
||||
raise Argument_Error
|
||||
with "cannot execute " & ASU.To_String (Print (First));
|
||||
end case;
|
||||
when others =>
|
||||
return Ast;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
is
|
||||
|
||||
function Readline (Prompt : in Interfaces.C.char_array)
|
||||
return Interfaces.C.Strings.chars_ptr
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr) is
|
||||
use Interfaces.C, Interfaces.C.Strings;
|
||||
function Readline (Prompt : in char_array) return chars_ptr
|
||||
with Import, Convention => C, External_Name => "readline";
|
||||
|
||||
procedure Add_History (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Add_History (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "add_history";
|
||||
|
||||
procedure Free (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Free (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "free";
|
||||
|
||||
Prompt : constant Interfaces.C.char_array
|
||||
:= Interfaces.C.To_C ("user> ");
|
||||
C_Line : Interfaces.C.Strings.chars_ptr;
|
||||
Prompt : constant char_array := To_C ("user> ");
|
||||
C_Line : chars_ptr;
|
||||
begin
|
||||
loop
|
||||
C_Line := Readline (Prompt);
|
||||
exit when C_Line = Interfaces.C.Strings.Null_Ptr;
|
||||
exit when C_Line = Null_Ptr;
|
||||
declare
|
||||
Line : constant String := Interfaces.C.Strings.Value (C_Line);
|
||||
Line : constant String := Value (C_Line);
|
||||
begin
|
||||
if Line /= "" then
|
||||
Add_History (C_Line);
|
||||
@ -240,87 +231,77 @@ procedure Step7_Quote is
|
||||
exception
|
||||
when Reader.Empty_Source =>
|
||||
null;
|
||||
when E : others =>
|
||||
when E : Argument_Error | Reader.Reader_Error
|
||||
| Environments.Unknown_Key =>
|
||||
Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
|
||||
-- but go on proceeding.
|
||||
-- Other exceptions are unexpected.
|
||||
end;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
end Interactive_Loop;
|
||||
|
||||
function Quasiquote (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type
|
||||
function Quasiquote (Ast : in Mal.T;
|
||||
Env : in Environments.Ptr) return Mal.T
|
||||
is (case Ast.Kind is
|
||||
when Types.Kind_Vector =>
|
||||
(Types.Kind_List, Atoms.No_Element, Quasiquote (Ast.L, Env)),
|
||||
-- When the test is updated, replace Kind_List with Kind_Vector.
|
||||
when Types.Kind_List =>
|
||||
when Kind_Vector => Quasiquote (Ast.L, Env),
|
||||
-- When the test is updated, replace Kind_List with Kind_Vector.
|
||||
when Kind_List =>
|
||||
(if 0 < Ast.L.Length
|
||||
and then Ast.L.Element (1).Kind = Types.Kind_Symbol
|
||||
and then Ast.L.Element (1).S = Names.Unquote
|
||||
and then Ast.L.Element (1).Kind = Kind_Symbol
|
||||
and then Ast.L.Element (1).Symbol = Symbols.Names.Unquote
|
||||
then Eval (Ast.L.Element (2), Env)
|
||||
else (Types.Kind_List, Atoms.No_Element, Quasiquote (Ast.L, Env))),
|
||||
else Quasiquote (Ast.L, Env)),
|
||||
when others => Ast);
|
||||
|
||||
function Quasiquote (List : in Lists.Ptr;
|
||||
Env : in Environments.Ptr) return Lists.Ptr
|
||||
is
|
||||
use Types;
|
||||
Buffer : Mal_Type_Vectors.Vector;
|
||||
Elt : Mal_Type;
|
||||
Env : in Environments.Ptr) return Mal.T is
|
||||
-- The final return concatenates these lists.
|
||||
R : Mal.T_Array (1 .. List.Length);
|
||||
begin
|
||||
for I in 1 .. List.Length loop
|
||||
Elt := List.Element (I);
|
||||
if Elt.Kind in Kind_List | Kind_Vector
|
||||
and then 0 < Elt.L.Length
|
||||
and then Elt.L.Element (1).Kind = Kind_Symbol
|
||||
and then Elt.L.Element (1).S = Names.Splice_Unquote
|
||||
for I in R'Range loop
|
||||
R (I) := List.Element (I);
|
||||
if R (I).Kind in Kind_List | Kind_Vector
|
||||
and then 0 < R (I).L.Length
|
||||
and then R (I).L.Element (1).Kind = Kind_Symbol
|
||||
and then R (I).L.Element (1).Symbol = Symbols.Names.Splice_Unquote
|
||||
then
|
||||
pragma Assert (Elt.L.Length = 2);
|
||||
Elt := Eval (Elt.L.Element (2), Env);
|
||||
pragma Assert (Elt.Kind = Kind_List);
|
||||
for J in 1 .. Elt.L.Length loop
|
||||
Buffer.Append (Elt.L.Element (J));
|
||||
end loop;
|
||||
if R (I).L.Length /= 2 then
|
||||
raise Argument_Error with "splice-unquote: expects 1 argument";
|
||||
end if;
|
||||
R (I) := Eval (R (I).L.Element (2), Env);
|
||||
if R (I).Kind /= Kind_List then
|
||||
raise Argument_Error with "splice-unquote: expects a list";
|
||||
end if;
|
||||
else
|
||||
Buffer.Append (Quasiquote (Elt, Env));
|
||||
R (I) := Lists.List (Mal.T_Array'(1 => Quasiquote (R (I), Env)));
|
||||
end if;
|
||||
end loop;
|
||||
return R : constant Lists.Ptr := Lists.Alloc (Natural (Buffer.Length)) do
|
||||
for I in 1 .. R.Length loop
|
||||
R.Replace_Element (I, Buffer.Element (I));
|
||||
end loop;
|
||||
end return;
|
||||
return Lists.Concat (R);
|
||||
end Quasiquote;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
use Types;
|
||||
Argv : Mal_Type (Kind_List);
|
||||
Repl : constant Environments.Ptr := Environments.Alloc;
|
||||
function Eval_Native (Args : in Mal_Type_Array) return Mal_Type is
|
||||
(Eval (Args (Args'First), Repl));
|
||||
Startup : constant String := "(do"
|
||||
& "(def! not (fn* (a) (if a false true)))"
|
||||
& "(def! load-file (fn* (f)"
|
||||
& " (eval (read-string (str ""(do "" (slurp f) "")"")))))"
|
||||
& ")";
|
||||
Repl : Environments.Ptr renames Environments.Repl;
|
||||
use Ada.Command_Line;
|
||||
begin
|
||||
Core.Add_Built_In_Functions (Repl, Eval'Unrestricted_Access);
|
||||
Repl.Set (Names.Eval, Mal_Type'(Kind_Native, Atoms.No_Element,
|
||||
Eval_Native'Unrestricted_Access));
|
||||
|
||||
Discard (Eval (Read ("(def! not (fn* (a) (if a false true)))"), Repl));
|
||||
Discard (Eval (Read ("(def! load-file (fn* (f) "
|
||||
& "(eval (read-string (str ""(do "" (slurp f) "")"")))))"), Repl));
|
||||
|
||||
if Ada.Command_Line.Argument_Count = 0 then
|
||||
Repl.Set (Names.Argv, Argv);
|
||||
Core.Eval_Ref := Eval'Unrestricted_Access;
|
||||
Discard (Eval (Read (Startup), Repl));
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Argument_Count);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := (Kind_String, ASU.To_Unbounded_String (Argument (I)));
|
||||
end loop;
|
||||
Repl.Set (Symbols.Constructor ("*ARGV*"), Lists.List (Args));
|
||||
end;
|
||||
if Argument_Count = 0 then
|
||||
Interactive_Loop (Repl);
|
||||
else
|
||||
Argv.L := Lists.Alloc (Ada.Command_Line.Argument_Count - 1);
|
||||
for I in 2 .. Ada.Command_Line.Argument_Count loop
|
||||
Argv.L.Replace_Element (I - 1,
|
||||
Mal_Type'(Kind_String, Atoms.No_Element,
|
||||
Strings.Alloc (Ada.Command_Line.Argument (I))));
|
||||
end loop;
|
||||
Repl.Set (Names.Argv, Argv);
|
||||
Discard (Eval (Read ("(load-file """ & Ada.Command_Line.Argument (1)
|
||||
& """)"), Repl));
|
||||
Discard (Eval (Read ("(load-file """ & Argument (1) & """)"), Repl));
|
||||
end if;
|
||||
end Step7_Quote;
|
||||
|
@ -1,282 +1,262 @@
|
||||
with Ada.Containers.Vectors;
|
||||
with Ada.Command_Line;
|
||||
with Ada.Exceptions;
|
||||
with Ada.Strings.Unbounded;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
with Interfaces.C.Strings; use type Interfaces.C.Strings.chars_ptr;
|
||||
with Atoms;
|
||||
with Interfaces.C.Strings;
|
||||
|
||||
with Core;
|
||||
with Environments;
|
||||
with Lists;
|
||||
with Names;
|
||||
with Printer;
|
||||
with Reader;
|
||||
with Strings; use type Strings.Ptr;
|
||||
with Types; use type Types.Kind_Type;
|
||||
with Types.Functions;
|
||||
with Types.Lists;
|
||||
with Types.Mal;
|
||||
with Types.Maps;
|
||||
with Types.Symbols.Names;
|
||||
|
||||
procedure Step8_Macros is
|
||||
|
||||
function Read (Source : in String) return Types.Mal_Type
|
||||
package ASU renames Ada.Strings.Unbounded;
|
||||
use Types;
|
||||
use type Symbols.Ptr;
|
||||
|
||||
function Read (Source : in String) return Mal.T
|
||||
renames Reader.Read_Str;
|
||||
|
||||
function Eval (Rec_Ast : in Types.Mal_Type;
|
||||
Rec_Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
Unable_To_Call : exception;
|
||||
function Eval (Ast0 : in Mal.T;
|
||||
Env0 : in Environments.Ptr) return Mal.T;
|
||||
|
||||
function Quasiquote (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
function Quasiquote (Ast : in Mal.T;
|
||||
Env : in Environments.Ptr) return Mal.T;
|
||||
function Quasiquote (List : in Lists.Ptr;
|
||||
Env : in Environments.Ptr) return Lists.Ptr
|
||||
with Inline;
|
||||
Env : in Environments.Ptr) return Mal.T with Inline;
|
||||
-- Handle vectors and lists not starting with unquote.
|
||||
-- Mergeing quote and quasiquote into eval with a flag triggering
|
||||
-- a different behaviour as done for macros in step8 would improve
|
||||
-- the performances significantly, but Kanaka finds that it breaks
|
||||
-- too much the step structure shared by all implementations.
|
||||
|
||||
function Print (Ast : in Types.Mal_Type;
|
||||
Print_Readably : in Boolean := True)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
function Print (Ast : in Mal.T;
|
||||
Readably : in Boolean := True) return ASU.Unbounded_String
|
||||
renames Printer.Pr_Str;
|
||||
|
||||
function Rep (Source : in String;
|
||||
Env : in Environments.Ptr)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env)))
|
||||
with Inline;
|
||||
Env : in Environments.Ptr) return ASU.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env))) with Inline;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
with Inline;
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr);
|
||||
|
||||
function Eval_Elements is new Lists.Generic_Eval (Environments.Ptr, Eval);
|
||||
function Eval_Elements is new Maps.Generic_Eval (Environments.Ptr, Eval);
|
||||
|
||||
-- Convenient when the result of eval is of no interest.
|
||||
procedure Discard (Ast : in Types.Mal_Type) is null;
|
||||
|
||||
-- Eval, with a profile compatible with Native_Function_Access.
|
||||
function Eval_Native (Args : in Types.Mal_Type_Array) return Types.Mal_Type;
|
||||
|
||||
package Mal_Type_Vectors is new Ada.Containers.Vectors
|
||||
(Index_Type => Positive,
|
||||
Element_Type => Types.Mal_Type,
|
||||
"=" => Types."=");
|
||||
procedure Discard (Ast : in Mal.T) is null;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function Eval (Rec_Ast : in Types.Mal_Type;
|
||||
Rec_Env : in Environments.Ptr) return Types.Mal_Type
|
||||
is
|
||||
use Types;
|
||||
Ast : Types.Mal_Type := Rec_Ast;
|
||||
Env : Environments.Ptr := Rec_Env;
|
||||
function Eval (Ast0 : in Mal.T;
|
||||
Env0 : in Environments.Ptr) return Mal.T is
|
||||
-- Use local variables, that can be rewritten when tail call
|
||||
-- optimization goes to <<Restart>>.
|
||||
Ast : Mal.T := Ast0;
|
||||
Env : Environments.Ptr := Env0.Copy_Pointer;
|
||||
Macroexpanding : Boolean := False;
|
||||
First : Mal_Type;
|
||||
First : Mal.T;
|
||||
begin
|
||||
<<Restart>>
|
||||
<<Restart>>
|
||||
-- Ada.Text_IO.New_Line;
|
||||
-- Ada.Text_IO.Put ("EVAL: ");
|
||||
-- Ada.Text_IO.Unbounded_IO.Put_Line (Print (Ast));
|
||||
-- Environments.Dump_Stack;
|
||||
case Ast.Kind is
|
||||
when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Kind_String
|
||||
| Kind_Keyword | Kind_Macro | Kind_Function | Kind_Native =>
|
||||
return Ast;
|
||||
|
||||
when Kind_Symbol =>
|
||||
return Env.Get (Ast.S);
|
||||
|
||||
return Env.Get (Ast.Symbol);
|
||||
when Kind_Map =>
|
||||
declare
|
||||
function F (X : Mal_Type) return Mal_Type is (Eval (X, Env));
|
||||
begin
|
||||
return (Kind_Map, Atoms.No_Element, Ast.Map.Map (F'Access));
|
||||
end;
|
||||
|
||||
return Eval_Elements (Ast.Map, Env);
|
||||
when Kind_Vector =>
|
||||
return R : constant Mal_Type := (Kind_Vector, Atoms.No_Element,
|
||||
Lists.Alloc (Ast.L.Length))
|
||||
do
|
||||
for I in 1 .. Ast.L.Length loop
|
||||
R.L.Replace_Element (I, Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
end return;
|
||||
|
||||
return (Kind_Vector, Eval_Elements (Ast.L, Env));
|
||||
when Kind_List =>
|
||||
if Ast.L.Length = 0 then
|
||||
return Ast;
|
||||
end if;
|
||||
|
||||
First := Ast.L.Element (1);
|
||||
|
||||
-- Special forms
|
||||
if First.Kind = Kind_Symbol then
|
||||
|
||||
if First.S = Names.Def then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (2).Kind = Kind_Symbol);
|
||||
return R : constant Mal_Type := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).S, R);
|
||||
if First.Kind /= Kind_Symbol then
|
||||
-- Evaluate First, in the less frequent case where it is
|
||||
-- not a symbol.
|
||||
First := Eval (First, Env);
|
||||
elsif First.Symbol = Symbols.Names.Def then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "def!: expects 2 arguments";
|
||||
elsif Ast.L.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "def!: arg 1 must be a symbol";
|
||||
end if;
|
||||
return R : constant Mal.T := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).Symbol, R);
|
||||
end return;
|
||||
elsif First.Symbol = Symbols.Names.Defmacro then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "defmacro!: expects 2 arguments";
|
||||
elsif Ast.L.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "defmacro!: arg 1 must be a symbol";
|
||||
end if;
|
||||
declare
|
||||
F : constant Mal.T := Eval (Ast.L.Element (3), Env);
|
||||
begin
|
||||
if F.Kind /= Kind_Function then
|
||||
raise Argument_Error with "defmacro!: expects a function";
|
||||
end if;
|
||||
return R : constant Mal.T := F.Function_Value.New_Macro do
|
||||
Env.Set (Ast.L.Element (2).Symbol, R);
|
||||
end return;
|
||||
|
||||
elsif First.S = Names.Defmacro then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (2).Kind = Kind_Symbol);
|
||||
F : constant Mal_Type := Eval (Ast.L.Element (3), Env);
|
||||
pragma Assert (F.Kind = Kind_Function);
|
||||
begin
|
||||
return R : constant Mal_Type
|
||||
:= (Kind => Kind_Macro,
|
||||
Meta => Atoms.No_Element,
|
||||
Mac_Formals => F.Formals,
|
||||
Mac_Expression => F.Expression)
|
||||
do
|
||||
Env.Set (Ast.L.Element (2).S, R);
|
||||
end return;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Mal_Do then
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (Ast.L.Length);
|
||||
goto Restart;
|
||||
|
||||
elsif First.S = Names.Fn then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (I).Kind = Kind_Symbol);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).L.Length < 1
|
||||
or else Names.Ampersand /=
|
||||
Ast.L.Element (2).L.Element (Ast.L.Element (2).L.Length).S);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length - 2 =>
|
||||
Ast.L.Element (2).L.Element (I).S /= Names.Ampersand);
|
||||
return (Kind => Kind_Function,
|
||||
Meta => Atoms.No_Element,
|
||||
Formals => Ast.L.Element (2).L,
|
||||
Expression => Atoms.Alloc (Ast.L.Element (3)),
|
||||
Environment => Env);
|
||||
|
||||
elsif First.S = Names.Mal_If then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length in 3 .. 4);
|
||||
Test : constant Mal_Type := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Boolean_Value,
|
||||
when others => True)
|
||||
then
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
elsif Ast.L.Length = 3 then
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
else
|
||||
Ast := Ast.L.Element (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Let then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
pragma Assert (Bindings.Length mod 2 = 0);
|
||||
begin
|
||||
Env.Replace_With_Subenv;
|
||||
Env.Increase_Capacity (Bindings.Length / 2);
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
pragma Assert
|
||||
(Bindings.Element (2 * I - 1).Kind = Kind_Symbol);
|
||||
Env.Set (Bindings.Element (2 * I - 1).S,
|
||||
Eval (Bindings.Element (2 * I), Env));
|
||||
end loop;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Mal_Do then
|
||||
if Ast.L.Length = 1 then
|
||||
raise Argument_Error with "do: expects at least 1 argument";
|
||||
end if;
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (Ast.L.Length);
|
||||
goto Restart;
|
||||
elsif First.Symbol = Symbols.Names.Fn then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "fn*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "fn*: arg 1 must be a list or vector";
|
||||
elsif (for some F in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (F).Kind /= Kind_Symbol)
|
||||
then
|
||||
raise Argument_Error with "fn*: arg 2 must contain symbols";
|
||||
end if;
|
||||
return Functions.New_Function (Ast.L.Element (2).L,
|
||||
Ast.L.Element (3), Env.New_Closure);
|
||||
elsif First.Symbol = Symbols.Names.Mal_If then
|
||||
if Ast.L.Length not in 3 .. 4 then
|
||||
raise Argument_Error with "if: expects 2 or 3 arguments";
|
||||
end if;
|
||||
declare
|
||||
Test : constant Mal.T := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Ada_Boolean,
|
||||
when others => True)
|
||||
then
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Macroexpand then
|
||||
pragma Assert (Ast.L.Length = 2);
|
||||
Macroexpanding := True;
|
||||
Ast := Ast.L.Element (2);
|
||||
goto Restart;
|
||||
|
||||
elsif First.S = Names.Quote then
|
||||
pragma Assert (Ast.L.Length = 2);
|
||||
return Ast.L.Element (2);
|
||||
|
||||
elsif First.S = Names.Quasiquote then
|
||||
pragma Assert (Ast.L.Length = 2);
|
||||
return Quasiquote (Ast.L.Element (2), Env);
|
||||
end if;
|
||||
end if;
|
||||
|
||||
-- No special form has been found, attempt to apply the
|
||||
-- first element to the rest of the list.
|
||||
declare
|
||||
Args : Mal_Type_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
First := Eval (First, Env);
|
||||
case First.Kind is
|
||||
|
||||
when Kind_Native =>
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
return First.Native.all (Args);
|
||||
|
||||
when Kind_Function =>
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
Env := Environments.Alloc (Outer => First.Environment);
|
||||
Env.Set_Binds (First.Formals, Args);
|
||||
Ast := First.Expression.Deref;
|
||||
goto Restart;
|
||||
|
||||
when Kind_Macro =>
|
||||
for I in Args'Range loop
|
||||
Args (I) := Ast.L.Element (I);
|
||||
end loop;
|
||||
declare
|
||||
New_Env : constant Environments.Ptr
|
||||
:= Environments.Alloc (Outer => Env);
|
||||
begin
|
||||
New_Env.Set_Binds (First.Mac_Formals, Args);
|
||||
Ast := Eval (First.Mac_Expression.Deref, New_Env);
|
||||
end;
|
||||
if Macroexpanding then
|
||||
return Ast;
|
||||
elsif Ast.L.Length = 3 then
|
||||
return Mal.Nil;
|
||||
else
|
||||
Ast := Ast.L.Element (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Let then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "let*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "let*: expects a list or vector";
|
||||
end if;
|
||||
declare
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
begin
|
||||
if Bindings.Length mod 2 /= 0 then
|
||||
raise Argument_Error with "let*: odd number of bindings";
|
||||
end if;
|
||||
Env.Replace_With_Sub;
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
if Bindings.Element (2 * I - 1).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "let*: keys must be symbols";
|
||||
end if;
|
||||
Env.Set (Bindings.Element (2 * I - 1).Symbol,
|
||||
Eval (Bindings.Element (2 * I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
|
||||
when others =>
|
||||
raise Unable_To_Call
|
||||
with Ada.Strings.Unbounded.To_String (Print (First));
|
||||
end case;
|
||||
end;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Macroexpand then
|
||||
if Ast.L.Length /= 2 then
|
||||
raise Argument_Error with "macroexpand: expects 1 argument";
|
||||
end if;
|
||||
Macroexpanding := True;
|
||||
Ast := Ast.L.Element (2);
|
||||
goto Restart;
|
||||
elsif First.Symbol = Symbols.Names.Quasiquote then
|
||||
if Ast.L.Length /= 2 then
|
||||
raise Argument_Error with "quasiquote: expects 1 argument";
|
||||
end if;
|
||||
return Quasiquote (Ast.L.Element (2), Env);
|
||||
elsif First.Symbol = Symbols.Names.Quote then
|
||||
if Ast.L.Length /= 2 then
|
||||
raise Argument_Error with "quote: expects 1 argument";
|
||||
end if;
|
||||
return Ast.L.Element (2);
|
||||
else
|
||||
-- Equivalent to First := Eval (First, Env), except that
|
||||
-- we already know enough to spare a recursive call in
|
||||
-- this frequent case.
|
||||
First := Env.Get (First.Symbol);
|
||||
end if;
|
||||
-- Apply phase.
|
||||
case First.Kind is
|
||||
when Kind_Builtin =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
return First.Builtin.all (Args);
|
||||
end;
|
||||
when Kind_Function =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
Env.Replace_With_Sub (First.Function_Value.Closure);
|
||||
First.Function_Value.Set_Binds (Env, Args);
|
||||
Ast := First.Function_Value.Expression;
|
||||
goto Restart;
|
||||
end;
|
||||
when Kind_Macro =>
|
||||
declare
|
||||
New_Env : constant Environments.Ptr := Env.Sub;
|
||||
begin
|
||||
First.Function_Value.Set_Binds (New_Env, Ast.L);
|
||||
Ast := Eval (First.Function_Value.Expression, New_Env);
|
||||
end;
|
||||
if Macroexpanding then
|
||||
return Ast;
|
||||
end if;
|
||||
goto Restart;
|
||||
when others =>
|
||||
raise Argument_Error
|
||||
with "cannot execute " & ASU.To_String (Print (First));
|
||||
end case;
|
||||
when others =>
|
||||
return Ast;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
is
|
||||
|
||||
function Readline (Prompt : in Interfaces.C.char_array)
|
||||
return Interfaces.C.Strings.chars_ptr
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr) is
|
||||
use Interfaces.C, Interfaces.C.Strings;
|
||||
function Readline (Prompt : in char_array) return chars_ptr
|
||||
with Import, Convention => C, External_Name => "readline";
|
||||
|
||||
procedure Add_History (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Add_History (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "add_history";
|
||||
|
||||
procedure Free (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Free (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "free";
|
||||
|
||||
Prompt : constant Interfaces.C.char_array
|
||||
:= Interfaces.C.To_C ("user> ");
|
||||
C_Line : Interfaces.C.Strings.chars_ptr;
|
||||
Prompt : constant char_array := To_C ("user> ");
|
||||
C_Line : chars_ptr;
|
||||
begin
|
||||
loop
|
||||
C_Line := Readline (Prompt);
|
||||
exit when C_Line = Interfaces.C.Strings.Null_Ptr;
|
||||
exit when C_Line = Null_Ptr;
|
||||
declare
|
||||
Line : constant String := Interfaces.C.Strings.Value (C_Line);
|
||||
Line : constant String := Value (C_Line);
|
||||
begin
|
||||
if Line /= "" then
|
||||
Add_History (C_Line);
|
||||
@ -286,98 +266,88 @@ procedure Step8_Macros is
|
||||
exception
|
||||
when Reader.Empty_Source =>
|
||||
null;
|
||||
when E : others =>
|
||||
when E : Argument_Error | Reader.Reader_Error
|
||||
| Environments.Unknown_Key =>
|
||||
Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
|
||||
-- but go on proceeding.
|
||||
-- Other exceptions are unexpected.
|
||||
end;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
end Interactive_Loop;
|
||||
|
||||
function Quasiquote (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type
|
||||
function Quasiquote (Ast : in Mal.T;
|
||||
Env : in Environments.Ptr) return Mal.T
|
||||
is (case Ast.Kind is
|
||||
when Types.Kind_Vector =>
|
||||
(Types.Kind_List, Atoms.No_Element, Quasiquote (Ast.L, Env)),
|
||||
-- When the test is updated, replace Kind_List with Kind_Vector.
|
||||
when Types.Kind_List =>
|
||||
when Kind_Vector => Quasiquote (Ast.L, Env),
|
||||
-- When the test is updated, replace Kind_List with Kind_Vector.
|
||||
when Kind_List =>
|
||||
(if 0 < Ast.L.Length
|
||||
and then Ast.L.Element (1).Kind = Types.Kind_Symbol
|
||||
and then Ast.L.Element (1).S = Names.Unquote
|
||||
and then Ast.L.Element (1).Kind = Kind_Symbol
|
||||
and then Ast.L.Element (1).Symbol = Symbols.Names.Unquote
|
||||
then Eval (Ast.L.Element (2), Env)
|
||||
else (Types.Kind_List, Atoms.No_Element, Quasiquote (Ast.L, Env))),
|
||||
else Quasiquote (Ast.L, Env)),
|
||||
when others => Ast);
|
||||
|
||||
function Quasiquote (List : in Lists.Ptr;
|
||||
Env : in Environments.Ptr) return Lists.Ptr
|
||||
is
|
||||
use Types;
|
||||
Buffer : Mal_Type_Vectors.Vector;
|
||||
Elt : Mal_Type;
|
||||
Env : in Environments.Ptr) return Mal.T is
|
||||
-- The final return concatenates these lists.
|
||||
R : Mal.T_Array (1 .. List.Length);
|
||||
begin
|
||||
for I in 1 .. List.Length loop
|
||||
Elt := List.Element (I);
|
||||
if Elt.Kind in Kind_List | Kind_Vector
|
||||
and then 0 < Elt.L.Length
|
||||
and then Elt.L.Element (1).Kind = Kind_Symbol
|
||||
and then Elt.L.Element (1).S = Names.Splice_Unquote
|
||||
for I in R'Range loop
|
||||
R (I) := List.Element (I);
|
||||
if R (I).Kind in Kind_List | Kind_Vector
|
||||
and then 0 < R (I).L.Length
|
||||
and then R (I).L.Element (1).Kind = Kind_Symbol
|
||||
and then R (I).L.Element (1).Symbol = Symbols.Names.Splice_Unquote
|
||||
then
|
||||
pragma Assert (Elt.L.Length = 2);
|
||||
Elt := Eval (Elt.L.Element (2), Env);
|
||||
pragma Assert (Elt.Kind = Kind_List);
|
||||
for J in 1 .. Elt.L.Length loop
|
||||
Buffer.Append (Elt.L.Element (J));
|
||||
end loop;
|
||||
if R (I).L.Length /= 2 then
|
||||
raise Argument_Error with "splice-unquote: expects 1 argument";
|
||||
end if;
|
||||
R (I) := Eval (R (I).L.Element (2), Env);
|
||||
if R (I).Kind /= Kind_List then
|
||||
raise Argument_Error with "splice-unquote: expects a list";
|
||||
end if;
|
||||
else
|
||||
Buffer.Append (Quasiquote (Elt, Env));
|
||||
R (I) := Lists.List (Mal.T_Array'(1 => Quasiquote (R (I), Env)));
|
||||
end if;
|
||||
end loop;
|
||||
return R : constant Lists.Ptr := Lists.Alloc (Natural (Buffer.Length)) do
|
||||
for I in 1 .. R.Length loop
|
||||
R.Replace_Element (I, Buffer.Element (I));
|
||||
end loop;
|
||||
end return;
|
||||
return Lists.Concat (R);
|
||||
end Quasiquote;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
use Types;
|
||||
Argv : Mal_Type (Kind_List);
|
||||
Repl : constant Environments.Ptr := Environments.Alloc;
|
||||
function Eval_Native (Args : in Mal_Type_Array) return Mal_Type is
|
||||
(Eval (Args (Args'First), Repl));
|
||||
Startup : constant String := "(do"
|
||||
& "(def! not (fn* (a) (if a false true)))"
|
||||
& "(def! load-file (fn* (f)"
|
||||
& " (eval (read-string (str ""(do "" (slurp f) "")"")))))"
|
||||
& "(defmacro! cond (fn* (& xs)"
|
||||
& " (if (> (count xs) 0)"
|
||||
& " (list 'if (first xs)"
|
||||
& " (if (> (count xs) 1) (nth xs 1)"
|
||||
& " (throw ""odd number of forms to cond""))"
|
||||
& " (cons 'cond (rest (rest xs)))))))"
|
||||
& "(defmacro! or (fn* (& xs)"
|
||||
& " (if (empty? xs) nil"
|
||||
& " (if (= 1 (count xs)) (first xs)"
|
||||
& " `(let* (or_FIXME ~(first xs))"
|
||||
& " (if or_FIXME or_FIXME (or ~@(rest xs))))))))"
|
||||
& ")";
|
||||
Repl : Environments.Ptr renames Environments.Repl;
|
||||
use Ada.Command_Line;
|
||||
begin
|
||||
Core.Add_Built_In_Functions (Repl, Eval'Unrestricted_Access);
|
||||
Repl.Set (Names.Eval, Mal_Type'(Kind_Native, Atoms.No_Element,
|
||||
Eval_Native'Unrestricted_Access));
|
||||
|
||||
Discard (Eval (Read ("(def! not (fn* (a) (if a false true)))"), Repl));
|
||||
Discard (Eval (Read ("(def! load-file (fn* (f) "
|
||||
& "(eval (read-string (str ""(do "" (slurp f) "")"")))))"), Repl));
|
||||
Discard (Eval (Read ("(defmacro! cond "
|
||||
& "(fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) "
|
||||
& "(if (> (count xs) 1) (nth xs 1) "
|
||||
& "(throw ""odd number of forms to cond"")) "
|
||||
& "(cons 'cond (rest (rest xs)))))))"), Repl));
|
||||
Discard (Eval (Read ("(defmacro! or (fn* (& xs) "
|
||||
& "(if (empty? xs) nil "
|
||||
& "(if (= 1 (count xs)) (first xs) "
|
||||
& "`(let* (or_FIXME ~(first xs)) "
|
||||
& "(if or_FIXME or_FIXME "
|
||||
& "(or ~@(rest xs))))))))"), Repl));
|
||||
|
||||
if Ada.Command_Line.Argument_Count = 0 then
|
||||
Repl.Set (Names.Argv, Argv);
|
||||
Core.Eval_Ref := Eval'Unrestricted_Access;
|
||||
Discard (Eval (Read (Startup), Repl));
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Argument_Count);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := (Kind_String, ASU.To_Unbounded_String (Argument (I)));
|
||||
end loop;
|
||||
Repl.Set (Symbols.Constructor ("*ARGV*"), Lists.List (Args));
|
||||
end;
|
||||
if Argument_Count = 0 then
|
||||
Interactive_Loop (Repl);
|
||||
else
|
||||
Argv.L := Lists.Alloc (Ada.Command_Line.Argument_Count - 1);
|
||||
for I in 2 .. Ada.Command_Line.Argument_Count loop
|
||||
Argv.L.Replace_Element (I - 1,
|
||||
Mal_Type'(Kind_String, Atoms.No_Element,
|
||||
Strings.Alloc (Ada.Command_Line.Argument (I))));
|
||||
end loop;
|
||||
Repl.Set (Names.Argv, Argv);
|
||||
Discard (Eval (Read ("(load-file """ & Ada.Command_Line.Argument (1)
|
||||
& """)"), Repl));
|
||||
Discard (Eval (Read ("(load-file """ & Argument (1) & """)"), Repl));
|
||||
end if;
|
||||
end Step8_Macros;
|
||||
|
@ -1,310 +1,303 @@
|
||||
with Ada.Containers.Vectors;
|
||||
with Ada.Command_Line;
|
||||
with Ada.Exceptions; use type Ada.Exceptions.Exception_Id;
|
||||
with Ada.Exceptions;
|
||||
with Ada.Strings.Unbounded;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
with Interfaces.C.Strings; use type Interfaces.C.Strings.chars_ptr;
|
||||
with Atoms;
|
||||
with Interfaces.C.Strings;
|
||||
|
||||
with Core;
|
||||
with Environments;
|
||||
with Lists;
|
||||
with Names;
|
||||
with Printer;
|
||||
with Reader;
|
||||
with Strings; use type Strings.Ptr;
|
||||
with Types; use type Types.Kind_Type;
|
||||
with Types.Functions;
|
||||
with Types.Lists;
|
||||
with Types.Mal;
|
||||
with Types.Maps;
|
||||
with Types.Symbols.Names;
|
||||
|
||||
procedure Step9_Try is
|
||||
|
||||
function Read (Source : in String) return Types.Mal_Type
|
||||
package ASU renames Ada.Strings.Unbounded;
|
||||
use Types;
|
||||
use type Symbols.Ptr;
|
||||
|
||||
function Read (Source : in String) return Mal.T
|
||||
renames Reader.Read_Str;
|
||||
|
||||
function Eval (Rec_Ast : in Types.Mal_Type;
|
||||
Rec_Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
Unable_To_Call : exception;
|
||||
function Eval (Ast0 : in Mal.T;
|
||||
Env0 : in Environments.Ptr) return Mal.T;
|
||||
|
||||
function Quasiquote (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
function Quasiquote (Ast : in Mal.T;
|
||||
Env : in Environments.Ptr) return Mal.T;
|
||||
function Quasiquote (List : in Lists.Ptr;
|
||||
Env : in Environments.Ptr) return Lists.Ptr
|
||||
with Inline;
|
||||
Env : in Environments.Ptr) return Mal.T with Inline;
|
||||
-- Handle vectors and lists not starting with unquote.
|
||||
-- Mergeing quote and quasiquote into eval with a flag triggering
|
||||
-- a different behaviour as done for macros in step8 would improve
|
||||
-- the performances significantly, but Kanaka finds that it breaks
|
||||
-- too much the step structure shared by all implementations.
|
||||
|
||||
function Print (Ast : in Types.Mal_Type;
|
||||
Print_Readably : in Boolean := True)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
function Print (Ast : in Mal.T;
|
||||
Readably : in Boolean := True) return ASU.Unbounded_String
|
||||
renames Printer.Pr_Str;
|
||||
|
||||
function Rep (Source : in String;
|
||||
Env : in Environments.Ptr)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env)))
|
||||
with Inline;
|
||||
Env : in Environments.Ptr) return ASU.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env))) with Inline;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
with Inline;
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr);
|
||||
|
||||
function Eval_Elements is new Lists.Generic_Eval (Environments.Ptr, Eval);
|
||||
function Eval_Elements is new Maps.Generic_Eval (Environments.Ptr, Eval);
|
||||
|
||||
-- Convenient when the result of eval is of no interest.
|
||||
procedure Discard (Ast : in Types.Mal_Type) is null;
|
||||
|
||||
-- Eval, with a profile compatible with Native_Function_Access.
|
||||
function Eval_Native (Args : in Types.Mal_Type_Array) return Types.Mal_Type;
|
||||
|
||||
package Mal_Type_Vectors is new Ada.Containers.Vectors
|
||||
(Index_Type => Positive,
|
||||
Element_Type => Types.Mal_Type,
|
||||
"=" => Types."=");
|
||||
procedure Discard (Ast : in Mal.T) is null;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function Eval (Rec_Ast : in Types.Mal_Type;
|
||||
Rec_Env : in Environments.Ptr) return Types.Mal_Type
|
||||
is
|
||||
use Types;
|
||||
Ast : Types.Mal_Type := Rec_Ast;
|
||||
Env : Environments.Ptr := Rec_Env;
|
||||
function Eval (Ast0 : in Mal.T;
|
||||
Env0 : in Environments.Ptr) return Mal.T is
|
||||
-- Use local variables, that can be rewritten when tail call
|
||||
-- optimization goes to <<Restart>>.
|
||||
Ast : Mal.T := Ast0;
|
||||
Env : Environments.Ptr := Env0.Copy_Pointer;
|
||||
Macroexpanding : Boolean := False;
|
||||
First : Mal_Type;
|
||||
First : Mal.T;
|
||||
begin
|
||||
<<Restart>>
|
||||
<<Restart>>
|
||||
-- Ada.Text_IO.New_Line;
|
||||
-- Ada.Text_IO.Put ("EVAL: ");
|
||||
-- Ada.Text_IO.Unbounded_IO.Put_Line (Print (Ast));
|
||||
-- Environments.Dump_Stack;
|
||||
case Ast.Kind is
|
||||
when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Kind_String
|
||||
| Kind_Keyword | Kind_Macro | Kind_Function | Kind_Native =>
|
||||
return Ast;
|
||||
|
||||
when Kind_Symbol =>
|
||||
return Env.Get (Ast.S);
|
||||
|
||||
return Env.Get (Ast.Symbol);
|
||||
when Kind_Map =>
|
||||
declare
|
||||
function F (X : Mal_Type) return Mal_Type is (Eval (X, Env));
|
||||
begin
|
||||
return (Kind_Map, Atoms.No_Element, Ast.Map.Map (F'Access));
|
||||
end;
|
||||
|
||||
return Eval_Elements (Ast.Map, Env);
|
||||
when Kind_Vector =>
|
||||
return R : constant Mal_Type := (Kind_Vector, Atoms.No_Element,
|
||||
Lists.Alloc (Ast.L.Length))
|
||||
do
|
||||
for I in 1 .. Ast.L.Length loop
|
||||
R.L.Replace_Element (I, Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
end return;
|
||||
|
||||
return (Kind_Vector, Eval_Elements (Ast.L, Env));
|
||||
when Kind_List =>
|
||||
if Ast.L.Length = 0 then
|
||||
return Ast;
|
||||
end if;
|
||||
|
||||
First := Ast.L.Element (1);
|
||||
|
||||
-- Special forms
|
||||
if First.Kind = Kind_Symbol then
|
||||
|
||||
if First.S = Names.Def then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (2).Kind = Kind_Symbol);
|
||||
return R : constant Mal_Type := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).S, R);
|
||||
if First.Kind /= Kind_Symbol then
|
||||
-- Evaluate First, in the less frequent case where it is
|
||||
-- not a symbol.
|
||||
First := Eval (First, Env);
|
||||
elsif First.Symbol = Symbols.Names.Def then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "def!: expects 2 arguments";
|
||||
elsif Ast.L.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "def!: arg 1 must be a symbol";
|
||||
end if;
|
||||
return R : constant Mal.T := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).Symbol, R);
|
||||
end return;
|
||||
elsif First.Symbol = Symbols.Names.Defmacro then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "defmacro!: expects 2 arguments";
|
||||
elsif Ast.L.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "defmacro!: arg 1 must be a symbol";
|
||||
end if;
|
||||
declare
|
||||
F : constant Mal.T := Eval (Ast.L.Element (3), Env);
|
||||
begin
|
||||
if F.Kind /= Kind_Function then
|
||||
raise Argument_Error with "defmacro!: expects a function";
|
||||
end if;
|
||||
return R : constant Mal.T := F.Function_Value.New_Macro do
|
||||
Env.Set (Ast.L.Element (2).Symbol, R);
|
||||
end return;
|
||||
|
||||
elsif First.S = Names.Defmacro then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (2).Kind = Kind_Symbol);
|
||||
F : constant Mal_Type := Eval (Ast.L.Element (3), Env);
|
||||
pragma Assert (F.Kind = Kind_Function);
|
||||
begin
|
||||
return R : constant Mal_Type
|
||||
:= (Kind => Kind_Macro,
|
||||
Meta => Atoms.No_Element,
|
||||
Mac_Formals => F.Formals,
|
||||
Mac_Expression => F.Expression)
|
||||
do
|
||||
Env.Set (Ast.L.Element (2).S, R);
|
||||
end return;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Mal_Do then
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (Ast.L.Length);
|
||||
goto Restart;
|
||||
|
||||
elsif First.S = Names.Fn then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (I).Kind = Kind_Symbol);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).L.Length < 1
|
||||
or else Names.Ampersand /=
|
||||
Ast.L.Element (2).L.Element (Ast.L.Element (2).L.Length).S);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length - 2 =>
|
||||
Ast.L.Element (2).L.Element (I).S /= Names.Ampersand);
|
||||
return (Kind => Kind_Function,
|
||||
Meta => Atoms.No_Element,
|
||||
Formals => Ast.L.Element (2).L,
|
||||
Expression => Atoms.Alloc (Ast.L.Element (3)),
|
||||
Environment => Env);
|
||||
|
||||
elsif First.S = Names.Mal_If then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length in 3 .. 4);
|
||||
Test : constant Mal_Type := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Boolean_Value,
|
||||
when others => True)
|
||||
then
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
elsif Ast.L.Length = 3 then
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
else
|
||||
Ast := Ast.L.Element (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Let then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
pragma Assert (Bindings.Length mod 2 = 0);
|
||||
begin
|
||||
Env.Replace_With_Subenv;
|
||||
Env.Increase_Capacity (Bindings.Length / 2);
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
pragma Assert
|
||||
(Bindings.Element (2 * I - 1).Kind = Kind_Symbol);
|
||||
Env.Set (Bindings.Element (2 * I - 1).S,
|
||||
Eval (Bindings.Element (2 * I), Env));
|
||||
end loop;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Mal_Do then
|
||||
if Ast.L.Length = 1 then
|
||||
raise Argument_Error with "do: expects at least 1 argument";
|
||||
end if;
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (Ast.L.Length);
|
||||
goto Restart;
|
||||
elsif First.Symbol = Symbols.Names.Fn then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "fn*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "fn*: arg 1 must be a list or vector";
|
||||
elsif (for some F in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (F).Kind /= Kind_Symbol)
|
||||
then
|
||||
raise Argument_Error with "fn*: arg 2 must contain symbols";
|
||||
end if;
|
||||
return Functions.New_Function (Ast.L.Element (2).L,
|
||||
Ast.L.Element (3), Env.New_Closure);
|
||||
elsif First.Symbol = Symbols.Names.Mal_If then
|
||||
if Ast.L.Length not in 3 .. 4 then
|
||||
raise Argument_Error with "if: expects 2 or 3 arguments";
|
||||
end if;
|
||||
declare
|
||||
Test : constant Mal.T := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Ada_Boolean,
|
||||
when others => True)
|
||||
then
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Macroexpand then
|
||||
pragma Assert (Ast.L.Length = 2);
|
||||
Macroexpanding := True;
|
||||
elsif Ast.L.Length = 3 then
|
||||
return Mal.Nil;
|
||||
else
|
||||
Ast := Ast.L.Element (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Let then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "let*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "let*: expects a list or vector";
|
||||
end if;
|
||||
declare
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
begin
|
||||
if Bindings.Length mod 2 /= 0 then
|
||||
raise Argument_Error with "let*: odd number of bindings";
|
||||
end if;
|
||||
Env.Replace_With_Sub;
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
if Bindings.Element (2 * I - 1).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "let*: keys must be symbols";
|
||||
end if;
|
||||
Env.Set (Bindings.Element (2 * I - 1).Symbol,
|
||||
Eval (Bindings.Element (2 * I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Macroexpand then
|
||||
if Ast.L.Length /= 2 then
|
||||
raise Argument_Error with "macroexpand: expects 1 argument";
|
||||
end if;
|
||||
Macroexpanding := True;
|
||||
Ast := Ast.L.Element (2);
|
||||
goto Restart;
|
||||
elsif First.Symbol = Symbols.Names.Quasiquote then
|
||||
if Ast.L.Length /= 2 then
|
||||
raise Argument_Error with "quasiquote: expects 1 argument";
|
||||
end if;
|
||||
return Quasiquote (Ast.L.Element (2), Env);
|
||||
elsif First.Symbol = Symbols.Names.Quote then
|
||||
if Ast.L.Length /= 2 then
|
||||
raise Argument_Error with "quote: expects 1 argument";
|
||||
end if;
|
||||
return Ast.L.Element (2);
|
||||
elsif First.Symbol = Symbols.Names.Try then
|
||||
if Ast.L.Length = 2 then
|
||||
Ast := Ast.L.Element (2);
|
||||
goto Restart;
|
||||
|
||||
elsif First.S = Names.Quote then
|
||||
pragma Assert (Ast.L.Length = 2);
|
||||
return Ast.L.Element (2);
|
||||
|
||||
elsif First.S = Names.Quasiquote then
|
||||
pragma Assert (Ast.L.Length = 2);
|
||||
return Quasiquote (Ast.L.Element (2), Env);
|
||||
|
||||
elsif First.S = Names.Try then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (3).Kind = Kind_List);
|
||||
A3 : constant Lists.Ptr := Ast.L.Element (3).L;
|
||||
pragma Assert (A3.Length = 3);
|
||||
pragma Assert (A3.Element (1).Kind = Kind_Symbol);
|
||||
pragma Assert (A3.Element (1).S = Names.Catch);
|
||||
pragma Assert (A3.Element (2).Kind = Kind_Symbol);
|
||||
elsif Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "try*: expects 1 or 2 arguments";
|
||||
elsif Ast.L.Element (3).Kind /= Kind_List then
|
||||
raise Argument_Error with "try*: argument 2 must be a list";
|
||||
end if;
|
||||
declare
|
||||
A3 : constant Lists.Ptr := Ast.L.Element (3).L;
|
||||
begin
|
||||
if A3.Length /= 3 then
|
||||
raise Argument_Error with "try*: arg 2 must have 3 elements";
|
||||
elsif A3.Element (1).Kind /= Kind_Symbol
|
||||
or else A3.Element (1).Symbol /= Symbols.Names.Catch
|
||||
then
|
||||
raise Argument_Error with "try*: arg 2 must be a catch*";
|
||||
elsif A3.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "catch*: expects a symbol";
|
||||
end if;
|
||||
begin
|
||||
return Eval (Ast.L.Element (2), Env);
|
||||
exception
|
||||
when E : others =>
|
||||
Env.Replace_With_Subenv;
|
||||
if Ada.Exceptions.Exception_Identity (E)
|
||||
= Core.Exception_Throwed'Identity
|
||||
then
|
||||
Env.Set (A3.Element (2).S, Core.Last_Exception);
|
||||
Core.Last_Exception := (Kind_Nil, Atoms.No_Element);
|
||||
else
|
||||
Env.Set (A3.Element (2).S, Mal_Type'
|
||||
(Kind_String, Atoms.No_Element, Strings.Alloc
|
||||
(Ada.Exceptions.Exception_Message (E))));
|
||||
end if;
|
||||
when E : Reader.Empty_Source | Argument_Error
|
||||
| Reader.Reader_Error | Environments.Unknown_Key =>
|
||||
Env.Replace_With_Sub;
|
||||
Env.Set (A3.Element (2).Symbol,
|
||||
Mal.T'(Kind_String, ASU.To_Unbounded_String
|
||||
(Ada.Exceptions.Exception_Message (E))));
|
||||
Ast := A3.Element (3);
|
||||
goto Restart;
|
||||
when Core.Exception_Throwed =>
|
||||
Env.Replace_With_Sub;
|
||||
Env.Set (A3.Element (2).Symbol, Core.Last_Exception);
|
||||
Core.Last_Exception := Mal.Nil;
|
||||
Ast := A3.Element (3);
|
||||
goto Restart;
|
||||
-- Other exceptions are unexpected.
|
||||
end;
|
||||
end if;
|
||||
end;
|
||||
else
|
||||
-- Equivalent to First := Eval (First, Env), except that
|
||||
-- we already know enough to spare a recursive call in
|
||||
-- this frequent case.
|
||||
First := Env.Get (First.Symbol);
|
||||
end if;
|
||||
|
||||
-- No special form has been found, attempt to apply the
|
||||
-- first element to the rest of the list.
|
||||
declare
|
||||
Args : Mal_Type_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
First := Eval (First, Env);
|
||||
case First.Kind is
|
||||
|
||||
when Kind_Native =>
|
||||
-- Apply phase.
|
||||
case First.Kind is
|
||||
when Kind_Builtin =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
return First.Native.all (Args);
|
||||
|
||||
when Kind_Function =>
|
||||
return First.Builtin.all (Args);
|
||||
end;
|
||||
when Kind_Function =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
Env := Environments.Alloc (Outer => First.Environment);
|
||||
Env.Set_Binds (First.Formals, Args);
|
||||
Ast := First.Expression.Deref;
|
||||
Env.Replace_With_Sub (First.Function_Value.Closure);
|
||||
First.Function_Value.Set_Binds (Env, Args);
|
||||
Ast := First.Function_Value.Expression;
|
||||
goto Restart;
|
||||
|
||||
when Kind_Macro =>
|
||||
for I in Args'Range loop
|
||||
Args (I) := Ast.L.Element (I);
|
||||
end loop;
|
||||
declare
|
||||
New_Env : constant Environments.Ptr
|
||||
:= Environments.Alloc (Outer => Env);
|
||||
begin
|
||||
New_Env.Set_Binds (First.Mac_Formals, Args);
|
||||
Ast := Eval (First.Mac_Expression.Deref, New_Env);
|
||||
end;
|
||||
if Macroexpanding then
|
||||
return Ast;
|
||||
end if;
|
||||
goto Restart;
|
||||
|
||||
when others =>
|
||||
raise Unable_To_Call
|
||||
with Ada.Strings.Unbounded.To_String (Print (First));
|
||||
end case;
|
||||
end;
|
||||
end;
|
||||
when Kind_Macro =>
|
||||
declare
|
||||
New_Env : constant Environments.Ptr := Env.Sub;
|
||||
begin
|
||||
First.Function_Value.Set_Binds (New_Env, Ast.L);
|
||||
Ast := Eval (First.Function_Value.Expression, New_Env);
|
||||
end;
|
||||
if Macroexpanding then
|
||||
return Ast;
|
||||
end if;
|
||||
goto Restart;
|
||||
when others =>
|
||||
raise Argument_Error
|
||||
with "cannot execute " & ASU.To_String (Print (First));
|
||||
end case;
|
||||
when others =>
|
||||
return Ast;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
is
|
||||
|
||||
function Readline (Prompt : in Interfaces.C.char_array)
|
||||
return Interfaces.C.Strings.chars_ptr
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr) is
|
||||
use Interfaces.C, Interfaces.C.Strings;
|
||||
function Readline (Prompt : in char_array) return chars_ptr
|
||||
with Import, Convention => C, External_Name => "readline";
|
||||
|
||||
procedure Add_History (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Add_History (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "add_history";
|
||||
|
||||
procedure Free (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Free (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "free";
|
||||
|
||||
Prompt : constant Interfaces.C.char_array
|
||||
:= Interfaces.C.To_C ("user> ");
|
||||
C_Line : Interfaces.C.Strings.chars_ptr;
|
||||
Prompt : constant char_array := To_C ("user> ");
|
||||
C_Line : chars_ptr;
|
||||
begin
|
||||
loop
|
||||
C_Line := Readline (Prompt);
|
||||
exit when C_Line = Interfaces.C.Strings.Null_Ptr;
|
||||
exit when C_Line = Null_Ptr;
|
||||
declare
|
||||
Line : constant String := Interfaces.C.Strings.Value (C_Line);
|
||||
Line : constant String := Value (C_Line);
|
||||
begin
|
||||
if Line /= "" then
|
||||
Add_History (C_Line);
|
||||
@ -314,98 +307,92 @@ procedure Step9_Try is
|
||||
exception
|
||||
when Reader.Empty_Source =>
|
||||
null;
|
||||
when E : others =>
|
||||
when E : Argument_Error | Reader.Reader_Error
|
||||
| Environments.Unknown_Key =>
|
||||
Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
|
||||
-- but go on proceeding.
|
||||
when Core.Exception_Throwed =>
|
||||
Ada.Text_IO.Put ("User exception: ");
|
||||
Ada.Text_IO.Unbounded_IO.Put_Line (Print (Core.Last_Exception));
|
||||
Core.Last_Exception := Mal.Nil;
|
||||
-- Other exceptions are unexpected.
|
||||
end;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
end Interactive_Loop;
|
||||
|
||||
function Quasiquote (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type
|
||||
function Quasiquote (Ast : in Mal.T;
|
||||
Env : in Environments.Ptr) return Mal.T
|
||||
is (case Ast.Kind is
|
||||
when Types.Kind_Vector =>
|
||||
(Types.Kind_List, Atoms.No_Element, Quasiquote (Ast.L, Env)),
|
||||
-- When the test is updated, replace Kind_List with Kind_Vector.
|
||||
when Types.Kind_List =>
|
||||
when Kind_Vector => Quasiquote (Ast.L, Env),
|
||||
-- When the test is updated, replace Kind_List with Kind_Vector.
|
||||
when Kind_List =>
|
||||
(if 0 < Ast.L.Length
|
||||
and then Ast.L.Element (1).Kind = Types.Kind_Symbol
|
||||
and then Ast.L.Element (1).S = Names.Unquote
|
||||
and then Ast.L.Element (1).Kind = Kind_Symbol
|
||||
and then Ast.L.Element (1).Symbol = Symbols.Names.Unquote
|
||||
then Eval (Ast.L.Element (2), Env)
|
||||
else (Types.Kind_List, Atoms.No_Element, Quasiquote (Ast.L, Env))),
|
||||
else Quasiquote (Ast.L, Env)),
|
||||
when others => Ast);
|
||||
|
||||
function Quasiquote (List : in Lists.Ptr;
|
||||
Env : in Environments.Ptr) return Lists.Ptr
|
||||
is
|
||||
use Types;
|
||||
Buffer : Mal_Type_Vectors.Vector;
|
||||
Elt : Mal_Type;
|
||||
Env : in Environments.Ptr) return Mal.T is
|
||||
-- The final return concatenates these lists.
|
||||
R : Mal.T_Array (1 .. List.Length);
|
||||
begin
|
||||
for I in 1 .. List.Length loop
|
||||
Elt := List.Element (I);
|
||||
if Elt.Kind in Kind_List | Kind_Vector
|
||||
and then 0 < Elt.L.Length
|
||||
and then Elt.L.Element (1).Kind = Kind_Symbol
|
||||
and then Elt.L.Element (1).S = Names.Splice_Unquote
|
||||
for I in R'Range loop
|
||||
R (I) := List.Element (I);
|
||||
if R (I).Kind in Kind_List | Kind_Vector
|
||||
and then 0 < R (I).L.Length
|
||||
and then R (I).L.Element (1).Kind = Kind_Symbol
|
||||
and then R (I).L.Element (1).Symbol = Symbols.Names.Splice_Unquote
|
||||
then
|
||||
pragma Assert (Elt.L.Length = 2);
|
||||
Elt := Eval (Elt.L.Element (2), Env);
|
||||
pragma Assert (Elt.Kind = Kind_List);
|
||||
for J in 1 .. Elt.L.Length loop
|
||||
Buffer.Append (Elt.L.Element (J));
|
||||
end loop;
|
||||
if R (I).L.Length /= 2 then
|
||||
raise Argument_Error with "splice-unquote: expects 1 argument";
|
||||
end if;
|
||||
R (I) := Eval (R (I).L.Element (2), Env);
|
||||
if R (I).Kind /= Kind_List then
|
||||
raise Argument_Error with "splice-unquote: expects a list";
|
||||
end if;
|
||||
else
|
||||
Buffer.Append (Quasiquote (Elt, Env));
|
||||
R (I) := Lists.List (Mal.T_Array'(1 => Quasiquote (R (I), Env)));
|
||||
end if;
|
||||
end loop;
|
||||
return R : constant Lists.Ptr := Lists.Alloc (Natural (Buffer.Length)) do
|
||||
for I in 1 .. R.Length loop
|
||||
R.Replace_Element (I, Buffer.Element (I));
|
||||
end loop;
|
||||
end return;
|
||||
return Lists.Concat (R);
|
||||
end Quasiquote;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
use Types;
|
||||
Argv : Mal_Type (Kind_List);
|
||||
Repl : constant Environments.Ptr := Environments.Alloc;
|
||||
function Eval_Native (Args : in Mal_Type_Array) return Mal_Type is
|
||||
(Eval (Args (Args'First), Repl));
|
||||
Startup : constant String := "(do"
|
||||
& "(def! not (fn* (a) (if a false true)))"
|
||||
& "(def! load-file (fn* (f)"
|
||||
& " (eval (read-string (str ""(do "" (slurp f) "")"")))))"
|
||||
& "(defmacro! cond (fn* (& xs)"
|
||||
& " (if (> (count xs) 0)"
|
||||
& " (list 'if (first xs)"
|
||||
& " (if (> (count xs) 1) (nth xs 1)"
|
||||
& " (throw ""odd number of forms to cond""))"
|
||||
& " (cons 'cond (rest (rest xs)))))))"
|
||||
& "(defmacro! or (fn* (& xs)"
|
||||
& " (if (empty? xs) nil"
|
||||
& " (if (= 1 (count xs)) (first xs)"
|
||||
& " `(let* (or_FIXME ~(first xs))"
|
||||
& " (if or_FIXME or_FIXME (or ~@(rest xs))))))))"
|
||||
& ")";
|
||||
Repl : Environments.Ptr renames Environments.Repl;
|
||||
use Ada.Command_Line;
|
||||
begin
|
||||
Core.Add_Built_In_Functions (Repl, Eval'Unrestricted_Access);
|
||||
Repl.Set (Names.Eval, Mal_Type'(Kind_Native, Atoms.No_Element,
|
||||
Eval_Native'Unrestricted_Access));
|
||||
|
||||
Discard (Eval (Read ("(def! not (fn* (a) (if a false true)))"), Repl));
|
||||
Discard (Eval (Read ("(def! load-file (fn* (f) "
|
||||
& "(eval (read-string (str ""(do "" (slurp f) "")"")))))"), Repl));
|
||||
Discard (Eval (Read ("(defmacro! cond "
|
||||
& "(fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) "
|
||||
& "(if (> (count xs) 1) (nth xs 1) "
|
||||
& "(throw ""odd number of forms to cond"")) "
|
||||
& "(cons 'cond (rest (rest xs)))))))"), Repl));
|
||||
Discard (Eval (Read ("(defmacro! or (fn* (& xs) "
|
||||
& "(if (empty? xs) nil "
|
||||
& "(if (= 1 (count xs)) (first xs) "
|
||||
& "`(let* (or_FIXME ~(first xs)) "
|
||||
& "(if or_FIXME or_FIXME "
|
||||
& "(or ~@(rest xs))))))))"), Repl));
|
||||
|
||||
if Ada.Command_Line.Argument_Count = 0 then
|
||||
Repl.Set (Names.Argv, Argv);
|
||||
Core.Eval_Ref := Eval'Unrestricted_Access;
|
||||
Discard (Eval (Read (Startup), Repl));
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Argument_Count);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := (Kind_String, ASU.To_Unbounded_String (Argument (I)));
|
||||
end loop;
|
||||
Repl.Set (Symbols.Constructor ("*ARGV*"), Lists.List (Args));
|
||||
end;
|
||||
if Argument_Count = 0 then
|
||||
Interactive_Loop (Repl);
|
||||
else
|
||||
Argv.L := Lists.Alloc (Ada.Command_Line.Argument_Count - 1);
|
||||
for I in 2 .. Ada.Command_Line.Argument_Count loop
|
||||
Argv.L.Replace_Element (I - 1,
|
||||
Mal_Type'(Kind_String, Atoms.No_Element,
|
||||
Strings.Alloc (Ada.Command_Line.Argument (I))));
|
||||
end loop;
|
||||
Repl.Set (Names.Argv, Argv);
|
||||
Discard (Eval (Read ("(load-file """ & Ada.Command_Line.Argument (1)
|
||||
& """)"), Repl));
|
||||
Discard (Eval (Read ("(load-file """ & Argument (1) & """)"), Repl));
|
||||
end if;
|
||||
end Step9_Try;
|
||||
|
@ -1,310 +1,312 @@
|
||||
with Ada.Containers.Vectors;
|
||||
with Ada.Command_Line;
|
||||
with Ada.Exceptions; use type Ada.Exceptions.Exception_Id;
|
||||
with Ada.Exceptions;
|
||||
with Ada.Strings.Unbounded;
|
||||
with Ada.Text_IO.Unbounded_IO;
|
||||
with Interfaces.C.Strings; use type Interfaces.C.Strings.chars_ptr;
|
||||
with Atoms;
|
||||
with Interfaces.C.Strings;
|
||||
|
||||
with Core;
|
||||
with Environments;
|
||||
with Lists;
|
||||
with Names;
|
||||
with Printer;
|
||||
with Reader;
|
||||
with Strings; use type Strings.Ptr;
|
||||
with Types; use type Types.Kind_Type;
|
||||
with Types.Functions;
|
||||
with Types.Lists;
|
||||
with Types.Mal;
|
||||
with Types.Maps;
|
||||
with Types.Symbols.Names;
|
||||
|
||||
procedure StepA_Mal is
|
||||
|
||||
function Read (Source : in String) return Types.Mal_Type
|
||||
package ASU renames Ada.Strings.Unbounded;
|
||||
use Types;
|
||||
use type Symbols.Ptr;
|
||||
|
||||
function Read (Source : in String) return Mal.T
|
||||
renames Reader.Read_Str;
|
||||
|
||||
function Eval (Rec_Ast : in Types.Mal_Type;
|
||||
Rec_Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
Unable_To_Call : exception;
|
||||
function Eval (Ast0 : in Mal.T;
|
||||
Env0 : in Environments.Ptr) return Mal.T;
|
||||
|
||||
function Quasiquote (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type;
|
||||
function Quasiquote (Ast : in Mal.T;
|
||||
Env : in Environments.Ptr) return Mal.T;
|
||||
function Quasiquote (List : in Lists.Ptr;
|
||||
Env : in Environments.Ptr) return Lists.Ptr
|
||||
with Inline;
|
||||
Env : in Environments.Ptr) return Mal.T with Inline;
|
||||
-- Handle vectors and lists not starting with unquote.
|
||||
-- Mergeing quote and quasiquote into eval with a flag triggering
|
||||
-- a different behaviour as done for macros in step8 would improve
|
||||
-- the performances significantly, but Kanaka finds that it breaks
|
||||
-- too much the step structure shared by all implementations.
|
||||
|
||||
function Print (Ast : in Types.Mal_Type;
|
||||
Print_Readably : in Boolean := True)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
function Print (Ast : in Mal.T;
|
||||
Readably : in Boolean := True) return ASU.Unbounded_String
|
||||
renames Printer.Pr_Str;
|
||||
|
||||
function Rep (Source : in String;
|
||||
Env : in Environments.Ptr)
|
||||
return Ada.Strings.Unbounded.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env)))
|
||||
with Inline;
|
||||
Env : in Environments.Ptr) return ASU.Unbounded_String
|
||||
is (Print (Eval (Read (Source), Env))) with Inline;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
with Inline;
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr);
|
||||
|
||||
function Eval_Elements is new Lists.Generic_Eval (Environments.Ptr, Eval);
|
||||
function Eval_Elements is new Maps.Generic_Eval (Environments.Ptr, Eval);
|
||||
|
||||
-- Convenient when the result of eval is of no interest.
|
||||
procedure Discard (Ast : in Types.Mal_Type) is null;
|
||||
|
||||
-- Eval, with a profile compatible with Native_Function_Access.
|
||||
function Eval_Native (Args : in Types.Mal_Type_Array) return Types.Mal_Type;
|
||||
|
||||
package Mal_Type_Vectors is new Ada.Containers.Vectors
|
||||
(Index_Type => Positive,
|
||||
Element_Type => Types.Mal_Type,
|
||||
"=" => Types."=");
|
||||
procedure Discard (Ast : in Mal.T) is null;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function Eval (Rec_Ast : in Types.Mal_Type;
|
||||
Rec_Env : in Environments.Ptr) return Types.Mal_Type
|
||||
is
|
||||
use Types;
|
||||
Ast : Types.Mal_Type := Rec_Ast;
|
||||
Env : Environments.Ptr := Rec_Env;
|
||||
function Eval (Ast0 : in Mal.T;
|
||||
Env0 : in Environments.Ptr) return Mal.T is
|
||||
-- Use local variables, that can be rewritten when tail call
|
||||
-- optimization goes to <<Restart>>.
|
||||
Ast : Mal.T := Ast0;
|
||||
Env : Environments.Ptr := Env0.Copy_Pointer;
|
||||
Macroexpanding : Boolean := False;
|
||||
First : Mal_Type;
|
||||
First : Mal.T;
|
||||
begin
|
||||
<<Restart>>
|
||||
<<Restart>>
|
||||
-- Ada.Text_IO.New_Line;
|
||||
-- Ada.Text_IO.Put ("EVAL: ");
|
||||
-- Ada.Text_IO.Unbounded_IO.Put_Line (Print (Ast));
|
||||
-- Environments.Dump_Stack;
|
||||
case Ast.Kind is
|
||||
when Kind_Nil | Kind_Atom | Kind_Boolean | Kind_Number | Kind_String
|
||||
| Kind_Keyword | Kind_Macro | Kind_Function | Kind_Native =>
|
||||
return Ast;
|
||||
|
||||
when Kind_Symbol =>
|
||||
return Env.Get (Ast.S);
|
||||
|
||||
return Env.Get (Ast.Symbol);
|
||||
when Kind_Map =>
|
||||
declare
|
||||
function F (X : Mal_Type) return Mal_Type is (Eval (X, Env));
|
||||
begin
|
||||
return (Kind_Map, Atoms.No_Element, Ast.Map.Map (F'Access));
|
||||
end;
|
||||
|
||||
return Eval_Elements (Ast.Map, Env);
|
||||
when Kind_Vector =>
|
||||
return R : constant Mal_Type := (Kind_Vector, Atoms.No_Element,
|
||||
Lists.Alloc (Ast.L.Length))
|
||||
do
|
||||
for I in 1 .. Ast.L.Length loop
|
||||
R.L.Replace_Element (I, Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
end return;
|
||||
|
||||
return (Kind_Vector, Eval_Elements (Ast.L, Env));
|
||||
when Kind_List =>
|
||||
if Ast.L.Length = 0 then
|
||||
return Ast;
|
||||
end if;
|
||||
|
||||
First := Ast.L.Element (1);
|
||||
|
||||
-- Special forms
|
||||
if First.Kind = Kind_Symbol then
|
||||
|
||||
if First.S = Names.Def then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (2).Kind = Kind_Symbol);
|
||||
return R : constant Mal_Type := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).S, R);
|
||||
if First.Kind /= Kind_Symbol then
|
||||
-- Evaluate First, in the less frequent case where it is
|
||||
-- not a symbol.
|
||||
First := Eval (First, Env);
|
||||
elsif First.Symbol = Symbols.Names.Def then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "def!: expects 2 arguments";
|
||||
elsif Ast.L.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "def!: arg 1 must be a symbol";
|
||||
end if;
|
||||
return R : constant Mal.T := Eval (Ast.L.Element (3), Env) do
|
||||
Env.Set (Ast.L.Element (2).Symbol, R);
|
||||
end return;
|
||||
elsif First.Symbol = Symbols.Names.Defmacro then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "defmacro!: expects 2 arguments";
|
||||
elsif Ast.L.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "defmacro!: arg 1 must be a symbol";
|
||||
end if;
|
||||
declare
|
||||
F : constant Mal.T := Eval (Ast.L.Element (3), Env);
|
||||
begin
|
||||
if F.Kind /= Kind_Function then
|
||||
raise Argument_Error with "defmacro!: expects a function";
|
||||
end if;
|
||||
return R : constant Mal.T := F.Function_Value.New_Macro do
|
||||
Env.Set (Ast.L.Element (2).Symbol, R);
|
||||
end return;
|
||||
|
||||
elsif First.S = Names.Defmacro then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (2).Kind = Kind_Symbol);
|
||||
F : constant Mal_Type := Eval (Ast.L.Element (3), Env);
|
||||
pragma Assert (F.Kind = Kind_Function);
|
||||
begin
|
||||
return R : constant Mal_Type
|
||||
:= (Kind => Kind_Macro,
|
||||
Meta => Atoms.No_Element,
|
||||
Mac_Formals => F.Formals,
|
||||
Mac_Expression => F.Expression)
|
||||
do
|
||||
Env.Set (Ast.L.Element (2).S, R);
|
||||
end return;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Mal_Do then
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (Ast.L.Length);
|
||||
goto Restart;
|
||||
|
||||
elsif First.S = Names.Fn then
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (I).Kind = Kind_Symbol);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).L.Length < 1
|
||||
or else Names.Ampersand /=
|
||||
Ast.L.Element (2).L.Element (Ast.L.Element (2).L.Length).S);
|
||||
pragma Assert
|
||||
(for all I in 1 .. Ast.L.Element (2).L.Length - 2 =>
|
||||
Ast.L.Element (2).L.Element (I).S /= Names.Ampersand);
|
||||
return (Kind => Kind_Function,
|
||||
Meta => Atoms.No_Element,
|
||||
Formals => Ast.L.Element (2).L,
|
||||
Expression => Atoms.Alloc (Ast.L.Element (3)),
|
||||
Environment => Env);
|
||||
|
||||
elsif First.S = Names.Mal_If then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length in 3 .. 4);
|
||||
Test : constant Mal_Type := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Boolean_Value,
|
||||
when others => True)
|
||||
then
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
elsif Ast.L.Length = 3 then
|
||||
return (Kind_Nil, Atoms.No_Element);
|
||||
else
|
||||
Ast := Ast.L.Element (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Let then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert
|
||||
(Ast.L.Element (2).Kind in Kind_List | Kind_Vector);
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
pragma Assert (Bindings.Length mod 2 = 0);
|
||||
begin
|
||||
Env.Replace_With_Subenv;
|
||||
Env.Increase_Capacity (Bindings.Length / 2);
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
pragma Assert
|
||||
(Bindings.Element (2 * I - 1).Kind = Kind_Symbol);
|
||||
Env.Set (Bindings.Element (2 * I - 1).S,
|
||||
Eval (Bindings.Element (2 * I), Env));
|
||||
end loop;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Mal_Do then
|
||||
if Ast.L.Length = 1 then
|
||||
raise Argument_Error with "do: expects at least 1 argument";
|
||||
end if;
|
||||
for I in 2 .. Ast.L.Length - 1 loop
|
||||
Discard (Eval (Ast.L.Element (I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (Ast.L.Length);
|
||||
goto Restart;
|
||||
elsif First.Symbol = Symbols.Names.Fn then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "fn*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "fn*: arg 1 must be a list or vector";
|
||||
elsif (for some F in 1 .. Ast.L.Element (2).L.Length =>
|
||||
Ast.L.Element (2).L.Element (F).Kind /= Kind_Symbol)
|
||||
then
|
||||
raise Argument_Error with "fn*: arg 2 must contain symbols";
|
||||
end if;
|
||||
return Functions.New_Function (Ast.L.Element (2).L,
|
||||
Ast.L.Element (3), Env.New_Closure);
|
||||
elsif First.Symbol = Symbols.Names.Mal_If then
|
||||
if Ast.L.Length not in 3 .. 4 then
|
||||
raise Argument_Error with "if: expects 2 or 3 arguments";
|
||||
end if;
|
||||
declare
|
||||
Test : constant Mal.T := Eval (Ast.L.Element (2), Env);
|
||||
begin
|
||||
if (case Test.Kind is
|
||||
when Kind_Nil => False,
|
||||
when Kind_Boolean => Test.Ada_Boolean,
|
||||
when others => True)
|
||||
then
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
end;
|
||||
|
||||
elsif First.S = Names.Macroexpand then
|
||||
pragma Assert (Ast.L.Length = 2);
|
||||
Macroexpanding := True;
|
||||
elsif Ast.L.Length = 3 then
|
||||
return Mal.Nil;
|
||||
else
|
||||
Ast := Ast.L.Element (4);
|
||||
goto Restart;
|
||||
end if;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Let then
|
||||
if Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "let*: expects 3 arguments";
|
||||
elsif Ast.L.Element (2).Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "let*: expects a list or vector";
|
||||
end if;
|
||||
declare
|
||||
Bindings : constant Lists.Ptr := Ast.L.Element (2).L;
|
||||
begin
|
||||
if Bindings.Length mod 2 /= 0 then
|
||||
raise Argument_Error with "let*: odd number of bindings";
|
||||
end if;
|
||||
Env.Replace_With_Sub;
|
||||
for I in 1 .. Bindings.Length / 2 loop
|
||||
if Bindings.Element (2 * I - 1).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "let*: keys must be symbols";
|
||||
end if;
|
||||
Env.Set (Bindings.Element (2 * I - 1).Symbol,
|
||||
Eval (Bindings.Element (2 * I), Env));
|
||||
end loop;
|
||||
Ast := Ast.L.Element (3);
|
||||
goto Restart;
|
||||
end;
|
||||
elsif First.Symbol = Symbols.Names.Macroexpand then
|
||||
if Ast.L.Length /= 2 then
|
||||
raise Argument_Error with "macroexpand: expects 1 argument";
|
||||
end if;
|
||||
Macroexpanding := True;
|
||||
Ast := Ast.L.Element (2);
|
||||
goto Restart;
|
||||
elsif First.Symbol = Symbols.Names.Quasiquote then
|
||||
if Ast.L.Length /= 2 then
|
||||
raise Argument_Error with "quasiquote: expects 1 argument";
|
||||
end if;
|
||||
return Quasiquote (Ast.L.Element (2), Env);
|
||||
elsif First.Symbol = Symbols.Names.Quote then
|
||||
if Ast.L.Length /= 2 then
|
||||
raise Argument_Error with "quote: expects 1 argument";
|
||||
end if;
|
||||
return Ast.L.Element (2);
|
||||
elsif First.Symbol = Symbols.Names.Try then
|
||||
if Ast.L.Length = 2 then
|
||||
Ast := Ast.L.Element (2);
|
||||
goto Restart;
|
||||
|
||||
elsif First.S = Names.Quote then
|
||||
pragma Assert (Ast.L.Length = 2);
|
||||
return Ast.L.Element (2);
|
||||
|
||||
elsif First.S = Names.Quasiquote then
|
||||
pragma Assert (Ast.L.Length = 2);
|
||||
return Quasiquote (Ast.L.Element (2), Env);
|
||||
|
||||
elsif First.S = Names.Try then
|
||||
declare
|
||||
pragma Assert (Ast.L.Length = 3);
|
||||
pragma Assert (Ast.L.Element (3).Kind = Kind_List);
|
||||
A3 : constant Lists.Ptr := Ast.L.Element (3).L;
|
||||
pragma Assert (A3.Length = 3);
|
||||
pragma Assert (A3.Element (1).Kind = Kind_Symbol);
|
||||
pragma Assert (A3.Element (1).S = Names.Catch);
|
||||
pragma Assert (A3.Element (2).Kind = Kind_Symbol);
|
||||
elsif Ast.L.Length /= 3 then
|
||||
raise Argument_Error with "try*: expects 1 or 2 arguments";
|
||||
elsif Ast.L.Element (3).Kind /= Kind_List then
|
||||
raise Argument_Error with "try*: argument 2 must be a list";
|
||||
end if;
|
||||
declare
|
||||
A3 : constant Lists.Ptr := Ast.L.Element (3).L;
|
||||
begin
|
||||
if A3.Length /= 3 then
|
||||
raise Argument_Error with "try*: arg 2 must have 3 elements";
|
||||
elsif A3.Element (1).Kind /= Kind_Symbol
|
||||
or else A3.Element (1).Symbol /= Symbols.Names.Catch
|
||||
then
|
||||
raise Argument_Error with "try*: arg 2 must be a catch*";
|
||||
elsif A3.Element (2).Kind /= Kind_Symbol then
|
||||
raise Argument_Error with "catch*: expects a symbol";
|
||||
end if;
|
||||
begin
|
||||
return Eval (Ast.L.Element (2), Env);
|
||||
exception
|
||||
when E : others =>
|
||||
Env.Replace_With_Subenv;
|
||||
if Ada.Exceptions.Exception_Identity (E)
|
||||
= Core.Exception_Throwed'Identity
|
||||
then
|
||||
Env.Set (A3.Element (2).S, Core.Last_Exception);
|
||||
Core.Last_Exception := (Kind_Nil, Atoms.No_Element);
|
||||
else
|
||||
Env.Set (A3.Element (2).S, Mal_Type'
|
||||
(Kind_String, Atoms.No_Element, Strings.Alloc
|
||||
(Ada.Exceptions.Exception_Message (E))));
|
||||
end if;
|
||||
when E : Reader.Empty_Source | Argument_Error
|
||||
| Reader.Reader_Error | Environments.Unknown_Key =>
|
||||
Env.Replace_With_Sub;
|
||||
Env.Set (A3.Element (2).Symbol,
|
||||
Mal.T'(Kind_String, ASU.To_Unbounded_String
|
||||
(Ada.Exceptions.Exception_Message (E))));
|
||||
Ast := A3.Element (3);
|
||||
goto Restart;
|
||||
when Core.Exception_Throwed =>
|
||||
Env.Replace_With_Sub;
|
||||
Env.Set (A3.Element (2).Symbol, Core.Last_Exception);
|
||||
Core.Last_Exception := Mal.Nil;
|
||||
Ast := A3.Element (3);
|
||||
goto Restart;
|
||||
-- Other exceptions are unexpected.
|
||||
end;
|
||||
end if;
|
||||
end;
|
||||
else
|
||||
-- Equivalent to First := Eval (First, Env), except that
|
||||
-- we already know enough to spare a recursive call in
|
||||
-- this frequent case.
|
||||
First := Env.Get (First.Symbol);
|
||||
end if;
|
||||
|
||||
-- No special form has been found, attempt to apply the
|
||||
-- first element to the rest of the list.
|
||||
declare
|
||||
Args : Mal_Type_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
First := Eval (First, Env);
|
||||
case First.Kind is
|
||||
|
||||
when Kind_Native =>
|
||||
-- Apply phase.
|
||||
case First.Kind is
|
||||
when Kind_Builtin =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
return First.Native.all (Args);
|
||||
|
||||
when Kind_Function =>
|
||||
return First.Builtin.all (Args);
|
||||
end;
|
||||
when Kind_Builtin_With_Meta =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
Env := Environments.Alloc (Outer => First.Environment);
|
||||
Env.Set_Binds (First.Formals, Args);
|
||||
Ast := First.Expression.Deref;
|
||||
goto Restart;
|
||||
|
||||
when Kind_Macro =>
|
||||
return First.Builtin_With_Meta.Data.all (Args);
|
||||
end;
|
||||
when Kind_Function =>
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Ast.L.Length);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := Ast.L.Element (I);
|
||||
Args (I) := Eval (Ast.L.Element (I), Env);
|
||||
end loop;
|
||||
declare
|
||||
New_Env : constant Environments.Ptr
|
||||
:= Environments.Alloc (Outer => Env);
|
||||
begin
|
||||
New_Env.Set_Binds (First.Mac_Formals, Args);
|
||||
Ast := Eval (First.Mac_Expression.Deref, New_Env);
|
||||
end;
|
||||
if Macroexpanding then
|
||||
return Ast;
|
||||
end if;
|
||||
Env.Replace_With_Sub (First.Function_Value.Closure);
|
||||
First.Function_Value.Set_Binds (Env, Args);
|
||||
Ast := First.Function_Value.Expression;
|
||||
goto Restart;
|
||||
|
||||
when others =>
|
||||
raise Unable_To_Call
|
||||
with Ada.Strings.Unbounded.To_String (Print (First));
|
||||
end case;
|
||||
end;
|
||||
end;
|
||||
when Kind_Macro =>
|
||||
declare
|
||||
New_Env : constant Environments.Ptr := Env.Sub;
|
||||
begin
|
||||
First.Function_Value.Set_Binds (New_Env, Ast.L);
|
||||
Ast := Eval (First.Function_Value.Expression, New_Env);
|
||||
end;
|
||||
if Macroexpanding then
|
||||
return Ast;
|
||||
end if;
|
||||
goto Restart;
|
||||
when others =>
|
||||
raise Argument_Error
|
||||
with "cannot execute " & ASU.To_String (Print (First));
|
||||
end case;
|
||||
when others =>
|
||||
return Ast;
|
||||
end case;
|
||||
end Eval;
|
||||
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr)
|
||||
is
|
||||
|
||||
function Readline (Prompt : in Interfaces.C.char_array)
|
||||
return Interfaces.C.Strings.chars_ptr
|
||||
procedure Interactive_Loop (Repl : in Environments.Ptr) is
|
||||
use Interfaces.C, Interfaces.C.Strings;
|
||||
function Readline (Prompt : in char_array) return chars_ptr
|
||||
with Import, Convention => C, External_Name => "readline";
|
||||
|
||||
procedure Add_History (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Add_History (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "add_history";
|
||||
|
||||
procedure Free (Line : in Interfaces.C.Strings.chars_ptr)
|
||||
procedure Free (Line : in chars_ptr)
|
||||
with Import, Convention => C, External_Name => "free";
|
||||
|
||||
Prompt : constant Interfaces.C.char_array
|
||||
:= Interfaces.C.To_C ("user> ");
|
||||
C_Line : Interfaces.C.Strings.chars_ptr;
|
||||
Prompt : constant char_array := To_C ("user> ");
|
||||
C_Line : chars_ptr;
|
||||
begin
|
||||
loop
|
||||
C_Line := Readline (Prompt);
|
||||
exit when C_Line = Interfaces.C.Strings.Null_Ptr;
|
||||
exit when C_Line = Null_Ptr;
|
||||
declare
|
||||
Line : constant String := Interfaces.C.Strings.Value (C_Line);
|
||||
Line : constant String := Value (C_Line);
|
||||
begin
|
||||
if Line /= "" then
|
||||
Add_History (C_Line);
|
||||
@ -314,104 +316,99 @@ procedure StepA_Mal is
|
||||
exception
|
||||
when Reader.Empty_Source =>
|
||||
null;
|
||||
when E : others =>
|
||||
when E : Argument_Error | Reader.Reader_Error
|
||||
| Environments.Unknown_Key =>
|
||||
Ada.Text_IO.Put_Line (Ada.Exceptions.Exception_Information (E));
|
||||
-- but go on proceeding.
|
||||
when Core.Exception_Throwed =>
|
||||
Ada.Text_IO.Put ("User exception: ");
|
||||
Ada.Text_IO.Unbounded_IO.Put_Line (Print (Core.Last_Exception));
|
||||
Core.Last_Exception := Mal.Nil;
|
||||
-- Other exceptions are unexpected.
|
||||
end;
|
||||
end loop;
|
||||
Ada.Text_IO.New_Line;
|
||||
end Interactive_Loop;
|
||||
|
||||
function Quasiquote (Ast : in Types.Mal_Type;
|
||||
Env : in Environments.Ptr) return Types.Mal_Type
|
||||
function Quasiquote (Ast : in Mal.T;
|
||||
Env : in Environments.Ptr) return Mal.T
|
||||
is (case Ast.Kind is
|
||||
when Types.Kind_Vector =>
|
||||
(Types.Kind_List, Atoms.No_Element, Quasiquote (Ast.L, Env)),
|
||||
-- When the test is updated, replace Kind_List with Kind_Vector.
|
||||
when Types.Kind_List =>
|
||||
when Kind_Vector => Quasiquote (Ast.L, Env),
|
||||
-- When the test is updated, replace Kind_List with Kind_Vector.
|
||||
when Kind_List =>
|
||||
(if 0 < Ast.L.Length
|
||||
and then Ast.L.Element (1).Kind = Types.Kind_Symbol
|
||||
and then Ast.L.Element (1).S = Names.Unquote
|
||||
and then Ast.L.Element (1).Kind = Kind_Symbol
|
||||
and then Ast.L.Element (1).Symbol = Symbols.Names.Unquote
|
||||
then Eval (Ast.L.Element (2), Env)
|
||||
else (Types.Kind_List, Atoms.No_Element, Quasiquote (Ast.L, Env))),
|
||||
else Quasiquote (Ast.L, Env)),
|
||||
when others => Ast);
|
||||
|
||||
function Quasiquote (List : in Lists.Ptr;
|
||||
Env : in Environments.Ptr) return Lists.Ptr
|
||||
is
|
||||
use Types;
|
||||
Buffer : Mal_Type_Vectors.Vector;
|
||||
Elt : Mal_Type;
|
||||
Env : in Environments.Ptr) return Mal.T is
|
||||
-- The final return concatenates these lists.
|
||||
R : Mal.T_Array (1 .. List.Length);
|
||||
begin
|
||||
for I in 1 .. List.Length loop
|
||||
Elt := List.Element (I);
|
||||
if Elt.Kind in Kind_List | Kind_Vector
|
||||
and then 0 < Elt.L.Length
|
||||
and then Elt.L.Element (1).Kind = Kind_Symbol
|
||||
and then Elt.L.Element (1).S = Names.Splice_Unquote
|
||||
for I in R'Range loop
|
||||
R (I) := List.Element (I);
|
||||
if R (I).Kind in Kind_List | Kind_Vector
|
||||
and then 0 < R (I).L.Length
|
||||
and then R (I).L.Element (1).Kind = Kind_Symbol
|
||||
and then R (I).L.Element (1).Symbol = Symbols.Names.Splice_Unquote
|
||||
then
|
||||
pragma Assert (Elt.L.Length = 2);
|
||||
Elt := Eval (Elt.L.Element (2), Env);
|
||||
pragma Assert (Elt.Kind = Kind_List);
|
||||
for J in 1 .. Elt.L.Length loop
|
||||
Buffer.Append (Elt.L.Element (J));
|
||||
end loop;
|
||||
if R (I).L.Length /= 2 then
|
||||
raise Argument_Error with "splice-unquote: expects 1 argument";
|
||||
end if;
|
||||
R (I) := Eval (R (I).L.Element (2), Env);
|
||||
if R (I).Kind /= Kind_List then
|
||||
raise Argument_Error with "splice-unquote: expects a list";
|
||||
end if;
|
||||
else
|
||||
Buffer.Append (Quasiquote (Elt, Env));
|
||||
R (I) := Lists.List (Mal.T_Array'(1 => Quasiquote (R (I), Env)));
|
||||
end if;
|
||||
end loop;
|
||||
return R : constant Lists.Ptr := Lists.Alloc (Natural (Buffer.Length)) do
|
||||
for I in 1 .. R.Length loop
|
||||
R.Replace_Element (I, Buffer.Element (I));
|
||||
end loop;
|
||||
end return;
|
||||
return Lists.Concat (R);
|
||||
end Quasiquote;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
use Types;
|
||||
Argv : Mal_Type (Kind_List);
|
||||
Repl : constant Environments.Ptr := Environments.Alloc;
|
||||
function Eval_Native (Args : in Mal_Type_Array) return Mal_Type is
|
||||
(Eval (Args (Args'First), Repl));
|
||||
Startup : constant String := "(do"
|
||||
& "(def! not (fn* (a) (if a false true)))"
|
||||
& "(def! load-file (fn* (f)"
|
||||
& " (eval (read-string (str ""(do "" (slurp f) "")"")))))"
|
||||
& "(defmacro! cond (fn* (& xs)"
|
||||
& " (if (> (count xs) 0)"
|
||||
& " (list 'if (first xs)"
|
||||
& " (if (> (count xs) 1) (nth xs 1)"
|
||||
& " (throw ""odd number of forms to cond""))"
|
||||
& " (cons 'cond (rest (rest xs)))))))"
|
||||
& "(def! *gensym-counter* (atom 0))"
|
||||
& "(def! gensym (fn* [] "
|
||||
& " (symbol (str ""G__"" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))"
|
||||
& "(defmacro! or (fn* (& xs)"
|
||||
& " (if (empty? xs) nil"
|
||||
& " (if (= 1 (count xs)) (first xs)"
|
||||
& " (let* (condvar (gensym))"
|
||||
& " `(let* (~condvar ~(first xs))"
|
||||
& " (if ~condvar ~condvar (or ~@(rest xs)))))))))"
|
||||
& "(def! *host-language* ""ada2"")"
|
||||
& ")";
|
||||
Repl : Environments.Ptr renames Environments.Repl;
|
||||
use Ada.Command_Line;
|
||||
begin
|
||||
Core.Add_Built_In_Functions (Repl, Eval'Unrestricted_Access);
|
||||
Repl.Set (Names.Eval, Mal_Type'(Kind_Native, Atoms.No_Element,
|
||||
Eval_Native'Unrestricted_Access));
|
||||
|
||||
Discard (Eval (Read ("(def! not (fn* (a) (if a false true)))"), Repl));
|
||||
Discard (Eval (Read ("(def! load-file (fn* (f) "
|
||||
& "(eval (read-string (str ""(do "" (slurp f) "")"")))))"), Repl));
|
||||
Discard (Eval (Read ("(defmacro! cond "
|
||||
& "(fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) "
|
||||
& "(if (> (count xs) 1) (nth xs 1) "
|
||||
& "(throw ""odd number of forms to cond"")) "
|
||||
& "(cons 'cond (rest (rest xs)))))))"), Repl));
|
||||
Discard (Eval (Read ("(def! *gensym-counter* (atom 0))"), Repl));
|
||||
Discard (Eval (Read ("(def! gensym (fn* [] (symbol (str ""G__"" "
|
||||
& "(swap! *gensym-counter* (fn* [x] (+ 1 x)))))))"), Repl));
|
||||
Discard (Eval (Read ("(defmacro! or (fn* (& xs) (if (empty? xs) nil "
|
||||
& "(if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) "
|
||||
& "`(let* (~condvar ~(first xs)) (if ~condvar ~condvar "
|
||||
& "(or ~@(rest xs)))))))))"), Repl));
|
||||
|
||||
Repl.Set (Names.Host_Language,
|
||||
Mal_Type'(Kind_Symbol, Atoms.No_Element, Names.Ada2));
|
||||
|
||||
if Ada.Command_Line.Argument_Count = 0 then
|
||||
Repl.Set (Names.Argv, Argv);
|
||||
Core.Eval_Ref := Eval'Unrestricted_Access;
|
||||
Discard (Eval (Read (Startup), Repl));
|
||||
declare
|
||||
Args : Mal.T_Array (2 .. Argument_Count);
|
||||
begin
|
||||
for I in Args'Range loop
|
||||
Args (I) := (Kind_String, ASU.To_Unbounded_String (Argument (I)));
|
||||
end loop;
|
||||
Repl.Set (Symbols.Constructor ("*ARGV*"), Lists.List (Args));
|
||||
end;
|
||||
if Argument_Count = 0 then
|
||||
Discard (Eval (Read ("(println (str ""Mal ["" *host-language* ""]""))"),
|
||||
Repl));
|
||||
Interactive_Loop (Repl);
|
||||
else
|
||||
Argv.L := Lists.Alloc (Ada.Command_Line.Argument_Count - 1);
|
||||
for I in 2 .. Ada.Command_Line.Argument_Count loop
|
||||
Argv.L.Replace_Element (I - 1,
|
||||
Mal_Type'(Kind_String, Atoms.No_Element,
|
||||
Strings.Alloc (Ada.Command_Line.Argument (I))));
|
||||
end loop;
|
||||
Repl.Set (Names.Argv, Argv);
|
||||
Discard (Eval (Read ("(load-file """ & Ada.Command_Line.Argument (1)
|
||||
& """)"), Repl));
|
||||
Discard (Eval (Read ("(load-file """ & Argument (1) & """)"), Repl));
|
||||
end if;
|
||||
end StepA_Mal;
|
||||
|
@ -1,62 +0,0 @@
|
||||
with Ada.Strings.Hash;
|
||||
|
||||
package body Strings is
|
||||
|
||||
Dict : Sets.Set;
|
||||
Empty_Hash : constant Ada.Containers.Hash_Type := Ada.Strings.Hash ("");
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
procedure Adjust (Object : in out Ptr) is
|
||||
begin
|
||||
if Sets.Has_Element (Object.Position) then
|
||||
Dict (Object.Position).Refs := Dict (Object.Position).Refs + 1;
|
||||
end if;
|
||||
end Adjust;
|
||||
|
||||
function Alloc (Source : in String) return Ptr
|
||||
is
|
||||
Inserted : Boolean;
|
||||
Position : Sets.Cursor;
|
||||
begin
|
||||
if Source /= "" then
|
||||
Sets.Insert (Dict,
|
||||
(Data => Source,
|
||||
Hash => Ada.Strings.Hash (Source),
|
||||
Last => Source'Length,
|
||||
Refs => 1),
|
||||
Position,
|
||||
Inserted);
|
||||
if not Inserted then
|
||||
Dict (Position).Refs := Dict (Position).Refs + 1;
|
||||
end if;
|
||||
end if;
|
||||
return (Ada.Finalization.Controlled with Position => Position);
|
||||
end Alloc;
|
||||
|
||||
function Deref (Source : in Ptr) return String is
|
||||
(if Sets.Has_Element (Source.Position)
|
||||
then Dict (Source.Position).Data
|
||||
else "");
|
||||
|
||||
procedure Finalize (Object : in out Ptr)
|
||||
is
|
||||
Refs : Positive;
|
||||
begin
|
||||
if Sets.Has_Element (Object.Position) then
|
||||
Refs := Dict (Object.Position).Refs;
|
||||
if 1 < Refs then
|
||||
Dict (Object.Position).Refs := Refs - 1;
|
||||
Object.Position := Sets.No_Element;
|
||||
else
|
||||
Sets.Delete (Dict, Object.Position);
|
||||
end if;
|
||||
end if;
|
||||
end Finalize;
|
||||
|
||||
function Hash (Source : in Ptr) return Ada.Containers.Hash_Type is
|
||||
(if Sets.Has_Element (Source.Position)
|
||||
then Dict (Source.Position).Hash
|
||||
else Empty_Hash);
|
||||
|
||||
end Strings;
|
@ -1,65 +0,0 @@
|
||||
with Ada.Containers;
|
||||
private with Ada.Containers.Indefinite_Hashed_Sets;
|
||||
private with Ada.Finalization;
|
||||
|
||||
package Strings is
|
||||
|
||||
pragma Elaborate_Body;
|
||||
|
||||
-- An abstraction similar to Ada.Strings.Unbounded, except that
|
||||
-- the type is immutable, and that only one instance is allocated
|
||||
-- with a given content. This avoids many allocations and
|
||||
-- deallocations, since symbols and keywords are expected to be
|
||||
-- used many times. Using this for all strings even if they are
|
||||
-- not used as keys in maps should not hurt.
|
||||
|
||||
-- As a side effect, some frequent string comparisons (with "def!"
|
||||
-- or "fn*" for example) will become a bit more efficient because
|
||||
-- comparing pointers is faster than comparing strings.
|
||||
|
||||
type Ptr is tagged private;
|
||||
Empty_String : constant Ptr; -- The default value.
|
||||
|
||||
function Alloc (Source : in String) return Ptr;
|
||||
|
||||
function Deref (Source : in Ptr) return String
|
||||
with Inline;
|
||||
|
||||
-- We make the hash value visible so that environments and maps do
|
||||
-- not need to recompute it.
|
||||
function Hash (Source : in Ptr) return Ada.Containers.Hash_Type
|
||||
with Inline;
|
||||
|
||||
private
|
||||
|
||||
type Element_Type (Last : Positive) is record
|
||||
Data : String (1 .. Last);
|
||||
Hash : Ada.Containers.Hash_Type;
|
||||
Refs : Positive;
|
||||
end record;
|
||||
|
||||
function Hash (Element : Element_Type) return Ada.Containers.Hash_Type
|
||||
is (Element.Hash)
|
||||
with Inline;
|
||||
|
||||
function Equivalent_Elements (Left, Right : Element_Type) return Boolean
|
||||
is (Left.Data = Right.Data)
|
||||
with Inline;
|
||||
|
||||
package Sets is new Ada.Containers.Indefinite_Hashed_Sets
|
||||
(Element_Type => Element_Type,
|
||||
Hash => Hash,
|
||||
Equivalent_Elements => Equivalent_Elements,
|
||||
"=" => "=");
|
||||
|
||||
type Ptr is new Ada.Finalization.Controlled with record
|
||||
Position : Sets.Cursor := Sets.No_Element;
|
||||
end record;
|
||||
overriding procedure Adjust (Object : in out Ptr) with Inline;
|
||||
overriding procedure Finalize (Object : in out Ptr) with Inline;
|
||||
-- Predefined equality is fine.
|
||||
|
||||
Empty_String : constant Ptr
|
||||
:= (Ada.Finalization.Controlled with Position => Sets.No_Element);
|
||||
|
||||
end Strings;
|
62
ada2/types-atoms.adb
Normal file
62
ada2/types-atoms.adb
Normal file
@ -0,0 +1,62 @@
|
||||
with Ada.Unchecked_Deallocation;
|
||||
|
||||
with Types.Mal;
|
||||
|
||||
package body Types.Atoms is
|
||||
|
||||
type Rec is limited record
|
||||
Refs : Natural;
|
||||
Data : Mal.T;
|
||||
end record;
|
||||
|
||||
procedure Free is new Ada.Unchecked_Deallocation (Rec, Acc);
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
procedure Adjust (Object : in out Ptr) is
|
||||
begin
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs + 1;
|
||||
end Adjust;
|
||||
|
||||
function Atom (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "atom: expects 1 argument"
|
||||
else
|
||||
(Kind => Kind_Atom,
|
||||
Atom => (Ada.Finalization.Controlled with
|
||||
Ref => new Rec'(Data => Args (Args'First),
|
||||
Refs => 1))));
|
||||
|
||||
function Deref (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "deref: expects 1 argument"
|
||||
elsif Args (Args'First).Kind /= Kind_Atom then
|
||||
raise Argument_Error with "deref: expects an atom"
|
||||
else
|
||||
(Args (Args'First).Atom.Ref.all.Data));
|
||||
|
||||
procedure Finalize (Object : in out Ptr) is
|
||||
begin
|
||||
if Object.Ref /= null and then 0 < Object.Ref.all.Refs then
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs - 1;
|
||||
if 0 < Object.Ref.all.Refs then
|
||||
Object.Ref := null;
|
||||
else
|
||||
Free (Object.Ref);
|
||||
end if;
|
||||
end if;
|
||||
end Finalize;
|
||||
|
||||
function Reset (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
if Args'Length /= 2 then
|
||||
raise Argument_Error with "reset: expects 2 arguments";
|
||||
elsif Args (Args'First).Kind /= Kind_Atom then
|
||||
raise Argument_Error with "reset: first argument must be an atom";
|
||||
else
|
||||
Args (Args'First).Atom.Ref.all.Data := Args (Args'Last);
|
||||
return Args (Args'Last);
|
||||
end if;
|
||||
end Reset;
|
||||
|
||||
end Types.Atoms;
|
35
ada2/types-atoms.ads
Normal file
35
ada2/types-atoms.ads
Normal file
@ -0,0 +1,35 @@
|
||||
private with Ada.Finalization;
|
||||
|
||||
limited with Types.Mal;
|
||||
|
||||
package Types.Atoms is
|
||||
|
||||
type Ptr is private;
|
||||
-- A wrapper for a pointer counting references.
|
||||
|
||||
-- The default value is invalid, new variables must be assigned
|
||||
-- immediately (a hidden discriminant would prevent this type to
|
||||
-- become a field inside Types.Mal.T, so we check this with a
|
||||
-- private invariant a fallback, an invariant in the private part
|
||||
-- checks that any created object is affected before use.
|
||||
|
||||
-- Assignment give another reference to the same storage.
|
||||
|
||||
-- Built-in functions.
|
||||
function Atom (Args : in Mal.T_Array) return Mal.T;
|
||||
function Deref (Args : in Mal.T_Array) return Mal.T;
|
||||
function Reset (Args : in Mal.T_Array) return Mal.T;
|
||||
|
||||
private
|
||||
|
||||
type Rec;
|
||||
type Acc is access Rec;
|
||||
type Ptr is new Ada.Finalization.Controlled with record
|
||||
Ref : Acc := null;
|
||||
end record
|
||||
with Invariant => Ref /= null;
|
||||
overriding procedure Adjust (Object : in out Ptr) with Inline;
|
||||
overriding procedure Finalize (Object : in out Ptr) with Inline;
|
||||
pragma Finalize_Storage_Only (Ptr);
|
||||
|
||||
end Types.Atoms;
|
53
ada2/types-builtins.adb
Normal file
53
ada2/types-builtins.adb
Normal file
@ -0,0 +1,53 @@
|
||||
with Ada.Unchecked_Deallocation;
|
||||
|
||||
with Types.Mal;
|
||||
|
||||
package body Types.Builtins is
|
||||
|
||||
type Rec is limited record
|
||||
Data : Ptr;
|
||||
Refs : Natural;
|
||||
Meta : Mal.T;
|
||||
end record;
|
||||
|
||||
procedure Free is new Ada.Unchecked_Deallocation (Rec, Acc);
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
procedure Adjust (Object : in out Ptr_With_Meta) is
|
||||
begin
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs + 1;
|
||||
end Adjust;
|
||||
|
||||
function Data (Item : in Ptr_With_Meta) return Ptr
|
||||
is (Item.Ref.all.Data);
|
||||
|
||||
procedure Finalize (Object : in out Ptr_With_Meta) is
|
||||
begin
|
||||
if Object.Ref /= null and then 0 < Object.Ref.all.Refs then
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs - 1;
|
||||
if 0 < Object.Ref.all.Refs then
|
||||
Object.Ref := null;
|
||||
else
|
||||
Free (Object.Ref);
|
||||
end if;
|
||||
end if;
|
||||
end Finalize;
|
||||
|
||||
function Meta (Item : in Ptr_With_Meta) return Mal.T
|
||||
is (Item.Ref.all.Meta);
|
||||
|
||||
function With_Meta (Data : in Ptr;
|
||||
Meta : in Mal.T) return Mal.T
|
||||
is (Kind_Builtin_With_Meta, (Ada.Finalization.Controlled with new Rec'
|
||||
(Data => Data,
|
||||
Meta => Meta,
|
||||
Refs => 1)));
|
||||
|
||||
function With_Meta (Data : in Ptr_With_Meta;
|
||||
Meta : in Mal.T) return Mal.T
|
||||
-- Do not try to reuse the memory. We can hope that this kind of
|
||||
-- nonsense will be rare.
|
||||
is (With_Meta (Data.Data, Meta));
|
||||
|
||||
end Types.Builtins;
|
46
ada2/types-builtins.ads
Normal file
46
ada2/types-builtins.ads
Normal file
@ -0,0 +1,46 @@
|
||||
private with Ada.Finalization;
|
||||
|
||||
limited with Types.Mal;
|
||||
|
||||
package Types.Builtins is
|
||||
|
||||
type Ptr is access function (Args : in Mal.T_Array) return Mal.T;
|
||||
-- This access type is efficient and sufficient for most purposes,
|
||||
-- as counting references is a waste of time for native functions,
|
||||
-- which are often used as atomic elements. The controlled type
|
||||
-- below is only useful when one has the silly idea to add
|
||||
-- metadata to a built-in.
|
||||
|
||||
type Ptr_With_Meta is tagged private;
|
||||
-- A wrapper for a pointer counting references.
|
||||
|
||||
-- The default value is invalid, new variables must be assigned
|
||||
-- immediately (a hidden discriminant would prevent this type to
|
||||
-- become a field inside Types.Mal.T, so we check this with a
|
||||
-- private invariant a fallback, an invariant in the private part
|
||||
-- checks that any created object is affected before use.
|
||||
|
||||
-- Assignment give another reference to the same storage.
|
||||
|
||||
function With_Meta (Data : in Ptr;
|
||||
Meta : in Mal.T) return Mal.T with Inline;
|
||||
function With_Meta (Data : in Ptr_With_Meta;
|
||||
Meta : in Mal.T) return Mal.T with Inline;
|
||||
function Meta (Item : in Ptr_With_Meta) return Mal.T with Inline;
|
||||
function Data (Item : in Ptr_With_Meta) return Ptr with Inline;
|
||||
|
||||
private
|
||||
|
||||
-- See README for the implementation of reference counting.
|
||||
|
||||
type Rec;
|
||||
type Acc is access Rec;
|
||||
type Ptr_With_Meta is new Ada.Finalization.Controlled with record
|
||||
Ref : Acc := null;
|
||||
end record
|
||||
with Invariant => Ref /= null;
|
||||
overriding procedure Adjust (Object : in out Ptr_With_Meta) with Inline;
|
||||
overriding procedure Finalize (Object : in out Ptr_With_Meta) with Inline;
|
||||
pragma Finalize_Storage_Only (Ptr_With_Meta);
|
||||
|
||||
end Types.Builtins;
|
171
ada2/types-functions.adb
Normal file
171
ada2/types-functions.adb
Normal file
@ -0,0 +1,171 @@
|
||||
with Ada.Strings.Unbounded;
|
||||
with Ada.Unchecked_Deallocation;
|
||||
|
||||
with Environments;
|
||||
with Printer;
|
||||
with Types.Lists;
|
||||
with Types.Mal;
|
||||
with Types.Symbols.Names;
|
||||
|
||||
package body Types.Functions is
|
||||
|
||||
subtype AFC is Ada.Finalization.Controlled;
|
||||
package ASU renames Ada.Strings.Unbounded;
|
||||
use type Types.Symbols.Ptr;
|
||||
|
||||
type Rec is limited record
|
||||
Refs : Natural := 1;
|
||||
Args : Lists.Ptr;
|
||||
Expr : Mal.T;
|
||||
Env : Environments.Closure_Ptr;
|
||||
Varargs : Boolean;
|
||||
Meta : Mal.T := Mal.Nil;
|
||||
end record;
|
||||
|
||||
procedure Free is new Ada.Unchecked_Deallocation (Rec, Acc);
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
procedure Adjust (Object : in out Ptr) is
|
||||
begin
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs + 1;
|
||||
end Adjust;
|
||||
|
||||
function Closure (Item : in Ptr) return Environments.Closure_Ptr
|
||||
is (Item.Ref.all.Env);
|
||||
|
||||
function Expression (Item : in Ptr) return Mal.T
|
||||
is (Item.Ref.all.Expr);
|
||||
|
||||
procedure Finalize (Object : in out Ptr) is
|
||||
begin
|
||||
if Object.Ref /= null and then 0 < Object.Ref.all.Refs then
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs - 1;
|
||||
if 0 < Object.Ref.all.Refs then
|
||||
Object.Ref := null;
|
||||
else
|
||||
Free (Object.Ref);
|
||||
end if;
|
||||
end if;
|
||||
end Finalize;
|
||||
|
||||
function Formals (Item : in Ptr) return Lists.Ptr
|
||||
is (Item.Ref.all.Args);
|
||||
|
||||
function Meta (Item : in Ptr) return Mal.T
|
||||
is (Item.Ref.all.Meta);
|
||||
|
||||
function New_Function (Formals : in Lists.Ptr;
|
||||
Expression : in Mal.T;
|
||||
Environment : in Environments.Closure_Ptr)
|
||||
return Mal.T
|
||||
is (Kind_Function,
|
||||
(AFC with new Rec'
|
||||
(Args => Formals,
|
||||
Expr => Expression,
|
||||
Env => Environment,
|
||||
Varargs => 1 < Formals.Length
|
||||
and then Formals.Element (Formals.Length - 1).Symbol
|
||||
= Symbols.Names.Ampersand,
|
||||
others => <>)));
|
||||
|
||||
function New_Macro (Item : in Ptr) return Mal.T is
|
||||
Old : Rec renames Item.Ref.all;
|
||||
Ref : Acc;
|
||||
begin
|
||||
pragma Assert (0 < Old.Refs);
|
||||
if Old.Refs = 1 then
|
||||
Ref := Item.Ref;
|
||||
Old.Refs := 2;
|
||||
Old.Env := Environments.Null_Closure;
|
||||
-- Finalize the previous closure.
|
||||
Old.Meta := Mal.Nil;
|
||||
else
|
||||
Ref := new Rec'(Args => Item.Ref.all.Args,
|
||||
Expr => Item.Ref.all.Expr,
|
||||
Varargs => Item.Ref.all.Varargs,
|
||||
others => <>);
|
||||
end if;
|
||||
return (Kind_Macro, (AFC with Ref));
|
||||
end New_Macro;
|
||||
|
||||
procedure Set_Binds (Item : in Ptr;
|
||||
Env : in Environments.Ptr;
|
||||
Args : in Mal.T_Array) is
|
||||
R : Rec renames Item.Ref.all;
|
||||
begin
|
||||
if R.Varargs then
|
||||
if Args'Length < R.Args.Length - 2 then
|
||||
raise Argument_Error with "expected "
|
||||
& ASU.To_String (Printer.Pr_Str ((Kind_List, R.Args)))
|
||||
& ", got" & Args'Length'Img;
|
||||
end if;
|
||||
for I in 1 .. R.Args.Length - 2 loop
|
||||
Env.Set (R.Args.Element (I).Symbol, Args (Args'First + I - 1));
|
||||
end loop;
|
||||
Env.Set (R.Args.Element (R.Args.Length).Symbol,
|
||||
Lists.List (Args (Args'First + R.Args.Length - 2 .. Args'Last)));
|
||||
else
|
||||
if Args'Length /= R.Args.Length then
|
||||
raise Argument_Error with "expected "
|
||||
& ASU.To_String (Printer.Pr_Str ((Kind_List, R.Args)))
|
||||
& ", got" & Args'Length'Img;
|
||||
end if;
|
||||
for I in 1 .. R.Args.Length loop
|
||||
Env.Set (R.Args.Element (I).Symbol, Args (Args'First + I - 1));
|
||||
end loop;
|
||||
end if;
|
||||
end Set_Binds;
|
||||
|
||||
procedure Set_Binds (Item : in Ptr;
|
||||
Env : in Environments.Ptr;
|
||||
Args : in Lists.Ptr) is
|
||||
R : Rec renames Item.Ref.all;
|
||||
begin
|
||||
if R.Varargs then
|
||||
if Args.Length - 1 < R.Args.Length - 2 then
|
||||
raise Argument_Error with "expected "
|
||||
& ASU.To_String (Printer.Pr_Str ((Kind_List, R.Args)))
|
||||
& ", got" & Natural'Image (Args.Length - 1);
|
||||
end if;
|
||||
for I in 1 .. R.Args.Length - 2 loop
|
||||
Env.Set (R.Args.Element (I).Symbol, Args.Element (1 + I));
|
||||
end loop;
|
||||
Env.Set (R.Args.Element (R.Args.Length).Symbol,
|
||||
Lists.Slice (Args, R.Args.Length));
|
||||
else
|
||||
if Args.Length - 1 /= R.Args.Length then
|
||||
raise Argument_Error with "expected "
|
||||
& ASU.To_String (Printer.Pr_Str ((Kind_List, R.Args)))
|
||||
& ", got" & Natural'Image (Args.Length - 1);
|
||||
end if;
|
||||
for I in 1 .. R.Args.Length loop
|
||||
Env.Set (R.Args.Element (I).Symbol, Args.Element (1 + I));
|
||||
end loop;
|
||||
end if;
|
||||
end Set_Binds;
|
||||
|
||||
function With_Meta (Data : in Ptr;
|
||||
Meta : in Mal.T)
|
||||
return Mal.T is
|
||||
Old : Rec renames Data.Ref.all;
|
||||
Ref : Acc;
|
||||
begin
|
||||
pragma Assert (0 < Old.Refs);
|
||||
if Old.Refs = 1 then
|
||||
Ref := Data.Ref;
|
||||
Old.Refs := 2;
|
||||
Old.Meta := Meta;
|
||||
else
|
||||
Ref := new Rec'(Args => Data.Ref.all.Args,
|
||||
Expr => Data.Ref.all.Expr,
|
||||
Env => Data.Ref.all.Env,
|
||||
Varargs => Data.Ref.all.Varargs,
|
||||
Meta => Meta,
|
||||
others => <>);
|
||||
|
||||
end if;
|
||||
return (Kind_Function, (AFC with Ref));
|
||||
end With_Meta;
|
||||
|
||||
end Types.Functions;
|
67
ada2/types-functions.ads
Normal file
67
ada2/types-functions.ads
Normal file
@ -0,0 +1,67 @@
|
||||
private with Ada.Finalization;
|
||||
|
||||
limited with Environments;
|
||||
limited with Types.Lists;
|
||||
limited with Types.Mal;
|
||||
|
||||
package Types.Functions is
|
||||
|
||||
type Ptr is tagged private;
|
||||
-- A wrapper for a pointer counting references.
|
||||
|
||||
-- The default value is invalid, new variables must be assigned
|
||||
-- immediately (a hidden discriminant would prevent this type to
|
||||
-- become a field inside Types.Mal.T, so we check this with a
|
||||
-- private invariant a fallback, an invariant in the private part
|
||||
-- checks that any created object is affected before use.
|
||||
|
||||
-- Assignment give another reference to the same storage.
|
||||
|
||||
function New_Function (Formals : in Lists.Ptr;
|
||||
Expression : in Mal.T;
|
||||
Environment : in Environments.Closure_Ptr)
|
||||
return Mal.T
|
||||
with Inline;
|
||||
|
||||
-- Equivalent to a sequence of Set with the formal parameters and
|
||||
-- Args elements, except for the handling of "&".
|
||||
-- May raise Argument_Count.
|
||||
-- For functions.
|
||||
procedure Set_Binds (Item : in Ptr;
|
||||
Env : in Environments.Ptr;
|
||||
Args : in Mal.T_Array);
|
||||
|
||||
function New_Macro (Item : in Ptr) return Mal.T with Inline;
|
||||
-- Set_Binds for macros.
|
||||
-- It skips the first element of Args.
|
||||
procedure Set_Binds (Item : in Ptr;
|
||||
Env : in Environments.Ptr;
|
||||
Args : in Lists.Ptr);
|
||||
|
||||
-- Used when printing, or applying with specific requirements,
|
||||
-- like allowing tail call optimization or macros.
|
||||
function Formals (Item : in Ptr) return Lists.Ptr with Inline;
|
||||
function Expression (Item : in Ptr) return Mal.T with Inline;
|
||||
function Closure (Item : in Ptr) return Environments.Closure_Ptr
|
||||
with Inline;
|
||||
|
||||
function Meta (Item : in Ptr) return Mal.T with inline;
|
||||
function With_Meta (Data : in Ptr;
|
||||
Meta : in Mal.T)
|
||||
return Mal.T with Inline;
|
||||
|
||||
private
|
||||
|
||||
-- See README for the implementation of reference counting.
|
||||
|
||||
type Rec;
|
||||
type Acc is access Rec;
|
||||
type Ptr is new Ada.Finalization.Controlled with record
|
||||
Ref : Acc := null;
|
||||
end record
|
||||
with Invariant => Ref /= null;
|
||||
overriding procedure Adjust (Object : in out Ptr) with Inline;
|
||||
overriding procedure Finalize (Object : in out Ptr) with Inline;
|
||||
pragma Finalize_Storage_Only (Ptr);
|
||||
|
||||
end Types.Functions;
|
260
ada2/types-lists.adb
Normal file
260
ada2/types-lists.adb
Normal file
@ -0,0 +1,260 @@
|
||||
with Ada.Unchecked_Deallocation;
|
||||
|
||||
with Types.Mal;
|
||||
|
||||
package body Types.Lists is
|
||||
|
||||
subtype AFC is Ada.Finalization.Controlled;
|
||||
use type Mal.T_Array;
|
||||
|
||||
type Rec (Last : Natural) is limited record
|
||||
Refs : Natural := 1;
|
||||
Meta : Mal.T := Mal.Nil;
|
||||
Data : Mal.T_Array (1 .. Last) := (others => Mal.Nil);
|
||||
end record;
|
||||
|
||||
procedure Free is new Ada.Unchecked_Deallocation (Rec, Acc);
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function "=" (Left, Right : in Ptr) return Boolean is
|
||||
-- Should become Left.Ref.all.Data = Right.Ref.all.Data when
|
||||
-- https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89178 is fixed.
|
||||
use type Mal.T;
|
||||
L : Rec renames Left.Ref.all;
|
||||
R : Rec renames Right.Ref.all;
|
||||
begin
|
||||
return L.Last = R.Last
|
||||
and then (for all I in 1 .. L.Last => L.Data (I) = R.Data (I));
|
||||
end "=";
|
||||
|
||||
function "&" (Left : in Mal.T_Array;
|
||||
Right : in Ptr) return Mal.T_Array
|
||||
is (Left & Right.Ref.all.Data);
|
||||
|
||||
procedure Adjust (Object : in out Ptr) is
|
||||
begin
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs + 1;
|
||||
end Adjust;
|
||||
|
||||
function Concat (Args : in Mal.T_Array) return Mal.T is
|
||||
Sum : Natural := 0;
|
||||
Ref : Acc;
|
||||
begin
|
||||
for Arg of Args loop
|
||||
if Arg.Kind not in Kind_List | Kind_Vector then
|
||||
raise Argument_Error with "concat: expects lists or vectors";
|
||||
end if;
|
||||
Sum := Sum + Arg.L.Ref.all.Last;
|
||||
end loop;
|
||||
Ref := new Rec (Sum);
|
||||
for Arg of reverse Args loop
|
||||
Ref.all.Data (Sum - Arg.L.Ref.all.Last + 1 .. Sum)
|
||||
:= Arg.L.Ref.all.Data;
|
||||
Sum := Sum - Arg.L.Ref.all.Last;
|
||||
end loop;
|
||||
pragma Assert (Sum = 0);
|
||||
return (Kind_List, (AFC with Ref));
|
||||
end Concat;
|
||||
|
||||
function Conj (Args : in Mal.T_Array) return Mal.T is
|
||||
Ref : Acc;
|
||||
begin
|
||||
if Args'Length = 0 then
|
||||
raise Argument_Error with "conj: expects at least 1 argument";
|
||||
end if;
|
||||
case Args (Args'First).Kind is
|
||||
when Kind_List =>
|
||||
Ref := new Rec
|
||||
(Args'Length - 1 + Args (Args'First).L.Ref.all.Last);
|
||||
Ref.all.Data (Args'Length .. Ref.all.Last)
|
||||
:= Args (Args'First).L.Ref.all.Data;
|
||||
for I in 1 .. Args'Length - 1 loop
|
||||
Ref.all.Data (I) := Args (Args'Last - I + 1);
|
||||
end loop;
|
||||
return (Kind_List, (AFC with Ref));
|
||||
when Kind_Vector =>
|
||||
return (Kind_Vector, (AFC with new Rec'
|
||||
(Last => Args'Length - 1 + Args (Args'First).L.Ref.all.Last,
|
||||
Data => Args (Args'First).L.Ref.all.Data
|
||||
& Args (Args'First + 1 .. Args'Last),
|
||||
others => <>)));
|
||||
when others =>
|
||||
raise Argument_Error with "conj: first arg must be list or vector";
|
||||
end case;
|
||||
end Conj;
|
||||
|
||||
function Cons (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
if Args'Length /= 2 then
|
||||
raise Argument_Error with "cons: expects 2 arguments";
|
||||
end if;
|
||||
case Args (Args'Last).Kind is
|
||||
when Kind_List | Kind_Vector =>
|
||||
return (Kind_List, (AFC with new Rec'
|
||||
(Last => 1 + Args (Args'Last).L.Ref.all.Last,
|
||||
Data => Args (Args'First) & Args (Args'Last).L.Ref.all.Data,
|
||||
others => <>)));
|
||||
when others =>
|
||||
raise Argument_Error with "cons: last arg must be list or vector";
|
||||
end case;
|
||||
end Cons;
|
||||
|
||||
function Count (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "count: expects 1 argument"
|
||||
else
|
||||
(case Args (Args'First).Kind is
|
||||
when Kind_Nil =>
|
||||
(Kind_Number, 0),
|
||||
when Kind_List | Kind_Vector =>
|
||||
(Kind_Number, Args (Args'First).L.Ref.all.Last),
|
||||
when others =>
|
||||
raise Argument_Error with "count: expects a list or vector"));
|
||||
|
||||
function Element (Container : in Ptr;
|
||||
Index : in Positive) return Mal.T
|
||||
is (Container.Ref.all.Data (Index));
|
||||
|
||||
procedure Finalize (Object : in out Ptr) is
|
||||
begin
|
||||
if Object.Ref /= null and then 0 < Object.Ref.all.Refs then
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs - 1;
|
||||
if 0 < Object.Ref.all.Refs then
|
||||
Object.Ref := null;
|
||||
else
|
||||
Free (Object.Ref);
|
||||
end if;
|
||||
end if;
|
||||
end Finalize;
|
||||
|
||||
function First (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "first: expects 1 argument"
|
||||
else
|
||||
(case Args (Args'First).Kind is
|
||||
when Kind_Nil =>
|
||||
Mal.Nil,
|
||||
when Kind_List | Kind_Vector =>
|
||||
(if Args (Args'First).L.Ref.all.Last = 0 then
|
||||
Mal.Nil
|
||||
else
|
||||
Args (Args'First).L.Ref.all.Data (1)),
|
||||
when others =>
|
||||
raise Argument_Error with "first: expects a list or vector"));
|
||||
|
||||
function Generic_Eval (Container : in Ptr;
|
||||
Env : in Env_Type)
|
||||
return Ptr is
|
||||
-- Take care that automatic deallocation happens if an
|
||||
-- exception is propagated by user code.
|
||||
Old : Rec renames Container.Ref.all;
|
||||
Ref : Acc;
|
||||
begin
|
||||
pragma Assert (0 < Old.Refs);
|
||||
if Old.Refs = 1 then
|
||||
Ref := Container.Ref;
|
||||
Old.Refs := 2;
|
||||
Old.Meta := Mal.Nil;
|
||||
else
|
||||
Ref := new Rec (Old.Last);
|
||||
end if;
|
||||
return R : constant Ptr := (AFC with Ref) do
|
||||
for I in Old.Data'Range loop
|
||||
Ref.all.Data (I) := Eval (Old.Data (I), Env);
|
||||
end loop;
|
||||
end return;
|
||||
end Generic_Eval;
|
||||
|
||||
function Is_Empty (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 1 then
|
||||
raise Argument_Error with "empty?: expects 1 argument"
|
||||
else
|
||||
(case Args (Args'First).Kind is
|
||||
when Kind_List | Kind_Vector =>
|
||||
(Kind_Boolean, Args (Args'First).L.Ref.all.Last = 0),
|
||||
when others =>
|
||||
raise Argument_Error with "empty?: expects a list or vector"));
|
||||
|
||||
function Length (Source : in Ptr) return Natural
|
||||
is (Source.Ref.all.Last);
|
||||
|
||||
function List (Args : in Mal.T_Array) return Mal.T
|
||||
is (Kind_List, (AFC with new Rec'(Data => Args,
|
||||
Last => Args'Length,
|
||||
others => <>)));
|
||||
|
||||
function Meta (Item : in Ptr) return Mal.T
|
||||
is (Item.Ref.all.Meta);
|
||||
|
||||
function Nth (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 2 then
|
||||
raise Argument_Error with "nth: expects 2 arguments"
|
||||
else
|
||||
(case Args (Args'First).Kind is
|
||||
when Kind_List | Kind_Vector =>
|
||||
(if Args (Args'First + 1).Kind /= Kind_Number then
|
||||
raise Argument_Error with "nth: last arg must be a number"
|
||||
elsif 1 + Args (Args'Last).Ada_Number
|
||||
in Args (Args'First).L.Ref.all.Data'Range
|
||||
then
|
||||
Args (Args'First).L.Ref.all.Data
|
||||
(1 + Args (Args'Last).Ada_Number)
|
||||
else
|
||||
raise Argument_Error with "nth: index out of bounds"),
|
||||
when others =>
|
||||
raise Argument_Error with "nth: expects a list or vector"));
|
||||
|
||||
function Rest (Args : in Mal.T_Array) return Mal.T
|
||||
is (Kind_List, (AFC with
|
||||
(if Args'Length /= 1 then
|
||||
raise Argument_Error with "rest: expects 1 argument"
|
||||
else
|
||||
(case Args (Args'First).Kind is
|
||||
when Kind_Nil =>
|
||||
new Rec (0),
|
||||
when Kind_List | Kind_Vector =>
|
||||
(if Args (Args'First).L.Ref.all.Last = 0 then
|
||||
new Rec (0)
|
||||
else
|
||||
new Rec'(Last => Args (Args'First).L.Ref.all.Last - 1,
|
||||
Data => Args (Args'First).L.Ref.all.Data
|
||||
(2 .. Args (Args'First).L.Ref.all.Last),
|
||||
others => <>)),
|
||||
when others =>
|
||||
raise Argument_Error with "rest: expects a list or vector"))));
|
||||
|
||||
function Slice (Item : in Ptr;
|
||||
Start : in Positive)
|
||||
return Mal.T
|
||||
is (Kind_List, (AFC with new Rec'
|
||||
(Last => Item.Ref.all.Last - Start + 1,
|
||||
Data => Item.Ref.all.Data (Start .. Item.Ref.all.Last),
|
||||
others => <>)));
|
||||
|
||||
function Vector (Args : in Mal.T_Array) return Mal.T
|
||||
is (Kind_Vector, (AFC with new Rec'(Data => Args,
|
||||
Last => Args'Length,
|
||||
others => <>)));
|
||||
|
||||
function With_Meta (Data : in Ptr;
|
||||
Meta : in Mal.T)
|
||||
return Ptr is
|
||||
Old : Rec renames Data.Ref.all;
|
||||
Ref : Acc;
|
||||
begin
|
||||
pragma Assert (0 < Old.Refs);
|
||||
if Old.Refs = 1 then
|
||||
Ref := Data.Ref;
|
||||
Old.Refs := 2;
|
||||
Old.Meta := Meta;
|
||||
else
|
||||
Ref := new Rec'(Last => Old.Last,
|
||||
Data => Old.Data,
|
||||
Meta => Meta,
|
||||
others => <>);
|
||||
end if;
|
||||
return (AFC with Ref);
|
||||
end With_Meta;
|
||||
|
||||
end Types.Lists;
|
83
ada2/types-lists.ads
Normal file
83
ada2/types-lists.ads
Normal file
@ -0,0 +1,83 @@
|
||||
private with Ada.Finalization;
|
||||
|
||||
limited with Types.Mal;
|
||||
|
||||
package Types.Lists is
|
||||
|
||||
type Ptr is tagged private;
|
||||
-- A wrapper for a pointer counting references.
|
||||
|
||||
-- The default value is invalid, new variables must be assigned
|
||||
-- immediately (a hidden discriminant would prevent this type to
|
||||
-- become a field inside Types.Mal.T, so we check this with a
|
||||
-- private invariant a fallback, an invariant in the private part
|
||||
-- checks that any created object is affected before use.
|
||||
|
||||
-- Assignment give another reference to the same storage.
|
||||
|
||||
-- Built-in functions.
|
||||
function Concat (Args : in Mal.T_Array) return Mal.T;
|
||||
function Conj (Args : in Mal.T_Array) return Mal.T;
|
||||
function Cons (Args : in Mal.T_Array) return Mal.T;
|
||||
function Count (Args : in Mal.T_Array) return Mal.T;
|
||||
function First (Args : in Mal.T_Array) return Mal.T;
|
||||
function Is_Empty (Args : in Mal.T_Array) return Mal.T;
|
||||
function List (Args : in Mal.T_Array) return Mal.T;
|
||||
function Nth (Args : in Mal.T_Array) return Mal.T;
|
||||
function Rest (Args : in Mal.T_Array) return Mal.T;
|
||||
function Vector (Args : in Mal.T_Array) return Mal.T;
|
||||
|
||||
function Length (Source : in Ptr) return Natural with Inline;
|
||||
|
||||
function Element (Container : in Ptr;
|
||||
Index : in Positive) return Mal.T
|
||||
with Inline;
|
||||
Index_Error : exception;
|
||||
|
||||
function "&" (Left : in Mal.T_Array;
|
||||
Right : in Ptr) return Mal.T_Array;
|
||||
-- Used to implement Core.Apply.
|
||||
|
||||
-- Used to evaluate each element of a list/vector.
|
||||
-- Eval is generic because units cannot depend on each other.
|
||||
generic
|
||||
type Env_Type (<>) is limited private;
|
||||
with function Eval (Ast : in Mal.T;
|
||||
Env : in Env_Type)
|
||||
return Mal.T;
|
||||
function Generic_Eval (Container : in Ptr;
|
||||
Env : in Env_Type)
|
||||
return Ptr;
|
||||
|
||||
-- Used to spare an intermediate copy for & in macro arguments.
|
||||
function Slice (Item : in Ptr;
|
||||
Start : in Positive)
|
||||
return Mal.T;
|
||||
|
||||
function Meta (Item : in Ptr) return Mal.T with Inline;
|
||||
function With_Meta (Data : in Ptr;
|
||||
Meta : in Mal.T)
|
||||
return Ptr;
|
||||
|
||||
private
|
||||
|
||||
-- It is tempting to use null to represent an empty list, but the
|
||||
-- performance is not improved much, and the code is more complex.
|
||||
-- In addition, the empty list may want to carry metadata.
|
||||
|
||||
-- Similarly, always providing a default value like a pointer to a
|
||||
-- static empty list would not gain much, and probably hide some
|
||||
-- bugs.
|
||||
|
||||
type Rec;
|
||||
type Acc is access Rec;
|
||||
type Ptr is new Ada.Finalization.Controlled with record
|
||||
Ref : Acc := null;
|
||||
end record
|
||||
with Invariant => Ref /= null;
|
||||
overriding procedure Adjust (Object : in out Ptr) with Inline;
|
||||
overriding procedure Finalize (Object : in out Ptr) with Inline;
|
||||
overriding function "=" (Left, Right : in Ptr) return Boolean;
|
||||
pragma Finalize_Storage_Only (Ptr);
|
||||
|
||||
end Types.Lists;
|
31
ada2/types-mal.adb
Normal file
31
ada2/types-mal.adb
Normal file
@ -0,0 +1,31 @@
|
||||
package body Types.Mal is
|
||||
|
||||
use type Ada.Strings.Unbounded.Unbounded_String;
|
||||
use type Lists.Ptr;
|
||||
use type Maps.Ptr;
|
||||
use type Symbols.Ptr;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function "=" (Left, Right : in T) return Boolean
|
||||
is (case Left.Kind is
|
||||
when Kind_Nil =>
|
||||
Right.Kind = Kind_Nil,
|
||||
when Kind_Boolean =>
|
||||
Right.Kind = Kind_Boolean
|
||||
and then Left.Ada_Boolean = Right.Ada_Boolean,
|
||||
when Kind_Number =>
|
||||
Right.Kind = Kind_Number and then Left.Ada_Number = Right.Ada_Number,
|
||||
when Kind_Symbol =>
|
||||
Right.Kind = Kind_Symbol and then Left.Symbol = Right.Symbol,
|
||||
-- Here is the part that differs from the predefined equality.
|
||||
when Kind_Keyword | Kind_String =>
|
||||
Right.Kind = Left.Kind and then Left.S = Right.S,
|
||||
when Kind_List | Kind_Vector =>
|
||||
Right.Kind in Kind_List | Kind_Vector and then Left.L = Right.L,
|
||||
when Kind_Map =>
|
||||
Right.Kind = Kind_Map and then Left.Map = Right.Map,
|
||||
when others =>
|
||||
False);
|
||||
|
||||
end Types.Mal;
|
84
ada2/types-mal.ads
Normal file
84
ada2/types-mal.ads
Normal file
@ -0,0 +1,84 @@
|
||||
with Ada.Strings.Unbounded;
|
||||
|
||||
with Types.Atoms;
|
||||
with Types.Builtins;
|
||||
with Types.Functions;
|
||||
with Types.Lists;
|
||||
with Types.Maps;
|
||||
with Types.Symbols;
|
||||
|
||||
package Types.Mal is
|
||||
|
||||
-- A type with a default value for the discriminant is the Ada
|
||||
-- equivalent of a C union. It uses a fixed size, and allows
|
||||
-- efficient arrays. A class hierarchy would make this impossible,
|
||||
-- for little gain.
|
||||
-- Native types may seem to consume too much memory, but
|
||||
-- 1/ they require no allocation/deallocation.
|
||||
-- 2/ the overhead would actually be higher with an intermediate
|
||||
-- reference (the size of the pointer plus the size of the native
|
||||
-- type, while an union uses the minimum of both and a single
|
||||
-- memory area ).
|
||||
-- Each instance has the size required for the largest possible
|
||||
-- value, so subtypes should attempt to reduce their size when
|
||||
-- possible (see Types.Symbols for such a compromise).
|
||||
|
||||
-- The idea is inspired from the Haskell and OCaml interpreters,
|
||||
-- which use a bit to distinguish pointers from integers. Ada
|
||||
-- allows to specify the bit position of each component, but
|
||||
-- generating such architecture-dependent definitions seems a lot
|
||||
-- of work for MAL.
|
||||
|
||||
-- The Ada tradition is to give explicit names to types, but this
|
||||
-- one will be used very often, and almost each package declares
|
||||
-- an "use Types;" clause, so Mal.T will do.
|
||||
|
||||
-- The only problem with a hidden discriminant is that "in out"
|
||||
-- parameters cannot be reaffected with a different discriminant.
|
||||
-- Eval would be more efficient with "in out" parameters than with
|
||||
-- "in" parameters and a result, because lots of reference
|
||||
-- counting would be spared, and the implementation would be able
|
||||
-- to reuse dynamic memory more efficiently. Environments, and
|
||||
-- some list/map operations already attempt such reuse behind the
|
||||
-- curtain.
|
||||
|
||||
-- This would obfuscate the implementation of a functional
|
||||
-- language, and require deep changes (the discriminant can be
|
||||
-- changed for an in out or access parameter).
|
||||
|
||||
type T (Kind : Kind_Type := Kind_Nil) is record
|
||||
case Kind is
|
||||
when Kind_Nil =>
|
||||
null;
|
||||
when Kind_Boolean =>
|
||||
Ada_Boolean : Boolean;
|
||||
when Kind_Number =>
|
||||
Ada_Number : Integer;
|
||||
when Kind_Atom =>
|
||||
Atom : Atoms.Ptr;
|
||||
when Kind_Keyword | Kind_String =>
|
||||
S : Ada.Strings.Unbounded.Unbounded_String;
|
||||
when Kind_Symbol =>
|
||||
Symbol : Symbols.Ptr;
|
||||
when Kind_List | Kind_Vector =>
|
||||
L : Lists.Ptr;
|
||||
when Kind_Map =>
|
||||
Map : Maps.Ptr;
|
||||
when Kind_Builtin =>
|
||||
Builtin : Builtins.Ptr;
|
||||
when Kind_Builtin_With_Meta =>
|
||||
Builtin_With_Meta : Builtins.Ptr_With_Meta;
|
||||
when Kind_Function | Kind_Macro =>
|
||||
Function_Value : Functions.Ptr;
|
||||
end case;
|
||||
end record;
|
||||
|
||||
-- Useful for recursive automatic definition of equality for
|
||||
-- composite types like the array type below.
|
||||
function "=" (Left, Right : in T) return Boolean with Inline;
|
||||
|
||||
Nil : constant T := (Kind => Kind_Nil);
|
||||
|
||||
type T_Array is array (Positive range <>) of T;
|
||||
|
||||
end Types.Mal;
|
266
ada2/types-maps.adb
Normal file
266
ada2/types-maps.adb
Normal file
@ -0,0 +1,266 @@
|
||||
with Ada.Containers.Hashed_Maps;
|
||||
with Ada.Strings.Unbounded.Hash;
|
||||
with Ada.Unchecked_Deallocation;
|
||||
|
||||
with Types.Lists;
|
||||
with Types.Mal;
|
||||
|
||||
package body Types.Maps is
|
||||
|
||||
subtype AFC is Ada.Finalization.Controlled;
|
||||
use type Ada.Containers.Count_Type;
|
||||
|
||||
function Hash (Item : in Mal.T) return Ada.Containers.Hash_Type
|
||||
with Inline, Pre => Item.Kind in Kind_Keyword | Kind_String;
|
||||
|
||||
package HM is new Ada.Containers.Hashed_Maps (Key_Type => Mal.T,
|
||||
Element_Type => Mal.T,
|
||||
Hash => Hash,
|
||||
Equivalent_Keys => Mal."=",
|
||||
"=" => Mal."=");
|
||||
use type HM.Map;
|
||||
|
||||
type Rec is limited record
|
||||
Refs : Natural := 1;
|
||||
Data : HM.Map := HM.Empty_Map;
|
||||
Meta : Mal.T := Mal.Nil;
|
||||
end record;
|
||||
|
||||
procedure Free is new Ada.Unchecked_Deallocation (Rec, Acc);
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function "=" (Left, Right : in Ptr) return Boolean
|
||||
is (Left.Ref.all.Data = Right.Ref.all.Data);
|
||||
|
||||
procedure Adjust (Object : in out Ptr) is
|
||||
begin
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs + 1;
|
||||
end Adjust;
|
||||
|
||||
function Assoc (Args : in Mal.T_Array) return Mal.T is
|
||||
Binds : constant Natural := Args'Length / 2;
|
||||
begin
|
||||
if Args'Length mod 2 /= 1 then
|
||||
raise Argument_Error with "assoc: expects an odd argument count";
|
||||
elsif Args (Args'First).Kind /= Kind_Map then
|
||||
raise Argument_Error with "assoc: first argument must be a map";
|
||||
elsif (for some I in 1 .. Binds => Args (Args'First + 2 * I - 1).Kind
|
||||
not in Kind_Keyword | Kind_String)
|
||||
then
|
||||
raise Argument_Error with "assoc: keys must be strings or symbols";
|
||||
end if;
|
||||
declare
|
||||
Old : Rec renames Args (Args'First).Map.Ref.all;
|
||||
Ref : Acc;
|
||||
begin
|
||||
pragma Assert (0 < Old.Refs);
|
||||
if Old.Refs = 1 then
|
||||
Ref := Args (Args'First).Map.Ref;
|
||||
Old.Refs := 2;
|
||||
Old.Meta := Mal.Nil;
|
||||
else
|
||||
Ref := new Rec'(Data => Old.Data, others => <>);
|
||||
end if;
|
||||
for I in 1 .. Binds loop
|
||||
Ref.all.Data.Include (Key => Args (Args'First + 2 * I - 1),
|
||||
New_Item => Args (Args'First + 2 * I));
|
||||
end loop;
|
||||
return (Kind_Map, (AFC with Ref));
|
||||
end;
|
||||
end Assoc;
|
||||
|
||||
function Contains (Args : in Mal.T_Array) return Mal.T
|
||||
is (if Args'Length /= 2 then
|
||||
raise Argument_Error with "contains: expects 2 arguments"
|
||||
elsif Args (Args'First).Kind /= Kind_Map then
|
||||
raise Argument_Error with "contains: first arguement must be a map"
|
||||
else (Kind_Boolean,
|
||||
Args (Args'First).Map.Ref.all.Data.Contains (Args (Args'Last))));
|
||||
|
||||
function Dissoc (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
if Args'Length = 0 then
|
||||
raise Argument_Error with "dissoc: expects at least 1 argument";
|
||||
elsif Args (Args'First).Kind /= Kind_Map then
|
||||
raise Argument_Error with "dissoc: first argument must be a map";
|
||||
elsif (for some I in Args'First + 1 .. Args'Last =>
|
||||
Args (I).Kind not in Kind_Keyword | Kind_String)
|
||||
then
|
||||
raise Argument_Error with "dissoc: keys must be strings or symbols";
|
||||
end if;
|
||||
declare
|
||||
Old : Rec renames Args (Args'First).Map.Ref.all;
|
||||
Ref : Acc;
|
||||
begin
|
||||
pragma Assert (0 < Old.Refs);
|
||||
if Old.Refs = 1 then
|
||||
Ref := Args (Args'First).Map.Ref;
|
||||
Old.Refs := 2;
|
||||
Old.Meta := Mal.Nil;
|
||||
else
|
||||
Ref := new Rec'(Data => Old.Data, others => <>);
|
||||
end if;
|
||||
for I in Args'First + 1 .. Args'Last loop
|
||||
Ref.all.Data.Exclude (Args (I));
|
||||
end loop;
|
||||
return (Kind_Map, (AFC with Ref));
|
||||
end;
|
||||
end Dissoc;
|
||||
|
||||
procedure Finalize (Object : in out Ptr) is
|
||||
begin
|
||||
if Object.Ref /= null and then 0 < Object.Ref.all.Refs then
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs - 1;
|
||||
if 0 < Object.Ref.all.Refs then
|
||||
Object.Ref := null;
|
||||
else
|
||||
Free (Object.Ref);
|
||||
end if;
|
||||
end if;
|
||||
end Finalize;
|
||||
|
||||
function Generic_Eval (Container : in Ptr;
|
||||
Env : in Env_Type)
|
||||
return Mal.T is
|
||||
-- Copy the whole hash in order to avoid recomputing the hash
|
||||
-- for each key, even if it implies unneeded calls to adjust
|
||||
-- and finalize for Mal_Type values.
|
||||
Old : Rec renames Container.Ref.all;
|
||||
Ref : Acc;
|
||||
begin
|
||||
pragma Assert (0 < Old.Refs);
|
||||
if Old.Refs = 1 then
|
||||
Ref := Container.Ref;
|
||||
Old.Refs := 2;
|
||||
Old.Meta := Mal.Nil;
|
||||
else
|
||||
Ref := new Rec'(Data => Container.Ref.all.Data, others => <>);
|
||||
end if;
|
||||
-- Prepare a valid structure before running user code. In case
|
||||
-- an exception is raised, we want memory to be deallocated.
|
||||
return R : constant Mal.T := (Kind_Map, (AFC with Ref)) do
|
||||
for Position in Ref.all.Data.Iterate loop
|
||||
Ref.all.Data.Replace_Element (Position,
|
||||
Eval (HM.Element (Position), Env));
|
||||
end loop;
|
||||
end return;
|
||||
end Generic_Eval;
|
||||
|
||||
function Get (Args : in Mal.T_Array) return Mal.T is
|
||||
Position : HM.Cursor;
|
||||
begin
|
||||
if Args'Length /= 2 then
|
||||
raise Argument_Error with "get: expects 2 arguments";
|
||||
elsif Args (Args'Last).Kind not in Kind_Keyword | Kind_String then
|
||||
raise Argument_Error with "get: key must be a keyword or string";
|
||||
end if;
|
||||
case Args (Args'First).Kind is
|
||||
when Kind_Nil =>
|
||||
return Mal.Nil;
|
||||
when Kind_Map =>
|
||||
Position
|
||||
:= Args (Args'First).Map.Ref.all.Data.Find (Args (Args'Last));
|
||||
if HM.Has_Element (Position) then
|
||||
return HM.Element (Position);
|
||||
else
|
||||
return Mal.Nil;
|
||||
end if;
|
||||
when others =>
|
||||
raise Argument_Error with "get: first argument must be a map";
|
||||
end case;
|
||||
end Get;
|
||||
|
||||
function Hash (Item : in Mal.T) return Ada.Containers.Hash_Type
|
||||
is (Ada.Strings.Unbounded.Hash (Item.S));
|
||||
|
||||
function Hash_Map (Args : in Mal.T_Array) return Mal.T is
|
||||
Binds : constant Natural := Args'Length / 2;
|
||||
Ref : Acc;
|
||||
begin
|
||||
if Args'Length mod 2 /= 0 then
|
||||
raise Argument_Error with "hash-map: expects an even argument count";
|
||||
elsif (for some I in 0 .. Binds - 1 => Args (Args'First + 2 * I).Kind
|
||||
not in Kind_Keyword | Kind_String)
|
||||
then
|
||||
raise Argument_Error with "hash-map: keys must be strings or symbols";
|
||||
end if;
|
||||
Ref := new Rec;
|
||||
Ref.all.Data.Reserve_Capacity (Ada.Containers.Count_Type (Binds));
|
||||
for I in 0 .. Binds - 1 loop
|
||||
Ref.all.Data.Include (Key => Args (Args'First + 2 * I),
|
||||
New_Item => Args (Args'First + 2 * I + 1));
|
||||
end loop;
|
||||
return (Kind_Map, (AFC with Ref));
|
||||
end Hash_Map;
|
||||
|
||||
procedure Iterate (Container : in Ptr) is
|
||||
begin
|
||||
for Position in Container.Ref.all.Data.Iterate loop
|
||||
Process (HM.Key (Position), HM.Element (Position));
|
||||
end loop;
|
||||
end Iterate;
|
||||
|
||||
function Keys (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
if Args'Length /= 1 then
|
||||
raise Argument_Error with "keys: expects 1 argument";
|
||||
elsif Args (Args'First).Kind /= Kind_Map then
|
||||
raise Argument_Error with "keys: first argument must a map";
|
||||
end if;
|
||||
declare
|
||||
A1 : HM.Map renames Args (Args'First).Map.Ref.all.Data;
|
||||
R : Mal.T_Array (1 .. Natural (A1.Length));
|
||||
I : Positive := 1;
|
||||
begin
|
||||
for Position in A1.Iterate loop
|
||||
R (I) := HM.Key (Position);
|
||||
I := I + 1;
|
||||
end loop;
|
||||
return Lists.List (R);
|
||||
end;
|
||||
end Keys;
|
||||
|
||||
function Meta (Container : in Ptr) return Mal.T
|
||||
is (Container.Ref.all.Meta);
|
||||
|
||||
function Vals (Args : in Mal.T_Array) return Mal.T is
|
||||
begin
|
||||
if Args'Length /= 1 then
|
||||
raise Argument_Error with "vals: expects 1 argument";
|
||||
elsif Args (Args'First).Kind /= Kind_Map then
|
||||
raise Argument_Error with "vals: first argument must be a map";
|
||||
end if;
|
||||
declare
|
||||
A1 : HM.Map renames Args (Args'First).Map.Ref.all.Data;
|
||||
R : Mal.T_Array (1 .. Natural (A1.Length));
|
||||
I : Positive := 1;
|
||||
begin
|
||||
for Element of A1 loop
|
||||
R (I) := Element;
|
||||
I := I + 1;
|
||||
end loop;
|
||||
return Lists.List (R);
|
||||
end;
|
||||
end Vals;
|
||||
|
||||
function With_Meta (Data : in Ptr;
|
||||
Meta : in Mal.T)
|
||||
return Mal.T is
|
||||
Old : Rec renames Data.Ref.all;
|
||||
Ref : Acc;
|
||||
begin
|
||||
pragma Assert (0 < Old.Refs);
|
||||
if Old.Refs = 1 then
|
||||
Ref := Data.Ref;
|
||||
Old.Refs := 2;
|
||||
Old.Meta := Meta;
|
||||
else
|
||||
Ref := new Rec'(Data => Old.Data,
|
||||
Meta => Meta,
|
||||
others => <>);
|
||||
end if;
|
||||
return (Kind_Map, (AFC with Ref));
|
||||
end With_Meta;
|
||||
|
||||
end Types.Maps;
|
68
ada2/types-maps.ads
Normal file
68
ada2/types-maps.ads
Normal file
@ -0,0 +1,68 @@
|
||||
private with Ada.Finalization;
|
||||
|
||||
limited with Types.Mal;
|
||||
|
||||
package Types.Maps is
|
||||
|
||||
type Ptr is tagged private;
|
||||
-- A wrapper for a pointer counting references.
|
||||
|
||||
-- The default value is invalid, new variables must be assigned
|
||||
-- immediately (a hidden discriminant would prevent this type to
|
||||
-- become a field inside Types.Mal.T, so we check this with a
|
||||
-- private invariant a fallback, an invariant in the private part
|
||||
-- checks that any created object is affected before use.
|
||||
|
||||
-- Assignment give another reference to the same storage.
|
||||
|
||||
-- Built-in functions.
|
||||
function Assoc (Args : in Mal.T_Array) return Mal.T;
|
||||
function Contains (Args : in Mal.T_Array) return Mal.T;
|
||||
function Dissoc (Args : in Mal.T_Array) return Mal.T;
|
||||
function Get (Args : in Mal.T_Array) return Mal.T;
|
||||
function Hash_Map (Args : in Mal.T_Array) return Mal.T;
|
||||
function Keys (Args : in Mal.T_Array) return Mal.T;
|
||||
function Vals (Args : in Mal.T_Array) return Mal.T;
|
||||
|
||||
-- A generic is better than an access to function because of
|
||||
-- https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89159
|
||||
|
||||
-- Used to evaluate each element of a map.
|
||||
-- Eval is generic because units cannot depend on each other.
|
||||
generic
|
||||
type Env_Type (<>) is limited private;
|
||||
with function Eval (Ast : in Mal.T;
|
||||
Env : in Env_Type)
|
||||
return Mal.T;
|
||||
function Generic_Eval (Container : in Ptr;
|
||||
Env : in Env_Type)
|
||||
return Mal.T;
|
||||
|
||||
-- Used to print a map.
|
||||
generic
|
||||
with procedure Process (Key : in Mal.T;
|
||||
Element : in Mal.T);
|
||||
procedure Iterate (Container : in Ptr);
|
||||
|
||||
function Meta (Container : in Ptr) return Mal.T with Inline;
|
||||
|
||||
function With_Meta (Data : in Ptr;
|
||||
Meta : in Mal.T)
|
||||
return Mal.T;
|
||||
|
||||
private
|
||||
|
||||
-- See README for the implementation of reference counting.
|
||||
|
||||
type Rec;
|
||||
type Acc is access Rec;
|
||||
type Ptr is new Ada.Finalization.Controlled with record
|
||||
Ref : Acc := null;
|
||||
end record
|
||||
with Invariant => Ref /= null;
|
||||
overriding procedure Adjust (Object : in out Ptr) with Inline;
|
||||
overriding procedure Finalize (Object : in out Ptr) with Inline;
|
||||
overriding function "=" (Left, Right : in Ptr) return Boolean with Inline;
|
||||
pragma Finalize_Storage_Only (Ptr);
|
||||
|
||||
end Types.Maps;
|
31
ada2/types-symbols-names.ads
Normal file
31
ada2/types-symbols-names.ads
Normal file
@ -0,0 +1,31 @@
|
||||
package Types.Symbols.Names is
|
||||
|
||||
-- These symbols are used once by Read/Eval/Print cycle. Declare
|
||||
-- them here in order to avoid an allocation and a desallocation
|
||||
-- during each call of eval.
|
||||
-- The built-in functions declared in Core will remain allocated
|
||||
-- during the lifetime of the program and do not require this.
|
||||
|
||||
-- A separate package is required because the constructor must be
|
||||
-- callable, and a child package makes sense because without this
|
||||
-- problem, these definition would be in Symbols.
|
||||
Ampersand : constant Ptr := Constructor ("&");
|
||||
Catch : constant Ptr := Constructor ("catch*");
|
||||
Def : constant Ptr := Constructor ("def!");
|
||||
Defmacro : constant Ptr := Constructor ("defmacro!");
|
||||
Fn : constant Ptr := Constructor ("fn*");
|
||||
Let : constant Ptr := Constructor ("let*");
|
||||
Macroexpand : constant Ptr := Constructor ("macroexpand");
|
||||
Mal_Do : constant Ptr := Constructor ("do");
|
||||
Mal_If : constant Ptr := Constructor ("if");
|
||||
Quasiquote : constant Ptr := Constructor ("quasiquote");
|
||||
Quote : constant Ptr := Constructor ("quote");
|
||||
Splice_Unquote : constant Ptr := Constructor ("splice-unquote");
|
||||
Try : constant Ptr := Constructor ("try*");
|
||||
Unquote : constant Ptr := Constructor ("unquote");
|
||||
|
||||
-- These are used by both Core and Reader. Spare a search.
|
||||
Deref : constant Ptr := Constructor ("deref");
|
||||
With_Meta : constant Ptr := Constructor ("with-meta");
|
||||
|
||||
end Types.Symbols.Names;
|
90
ada2/types-symbols.adb
Normal file
90
ada2/types-symbols.adb
Normal file
@ -0,0 +1,90 @@
|
||||
with Ada.Containers.Ordered_Sets;
|
||||
with Ada.Strings.Hash;
|
||||
with Ada.Unchecked_Deallocation;
|
||||
|
||||
package body Types.Symbols is
|
||||
|
||||
-- For the global dictionnary of symbols, an ordered set seems
|
||||
-- better than a hash map.
|
||||
|
||||
type Rec (Last : Positive) is limited record
|
||||
Refs : Natural;
|
||||
Hash : Ada.Containers.Hash_Type;
|
||||
Data : String (1 .. Last);
|
||||
end record;
|
||||
procedure Free is new Ada.Unchecked_Deallocation (Rec, Acc);
|
||||
|
||||
function "<" (Left, Right : in Acc) return Boolean with Inline;
|
||||
function Eq (Left, Right : in Acc) return Boolean with Inline;
|
||||
-- It would be unwise to name this function "=" and override the
|
||||
-- predefined equality for Acc.
|
||||
-- We only search by key and insert new elements, so this should
|
||||
-- always return False.
|
||||
package Sets is new Ada.Containers.Ordered_Sets (Element_Type => Acc,
|
||||
"<" => "<",
|
||||
"=" => Eq);
|
||||
|
||||
function Key (Item : in Acc) return String with Inline;
|
||||
package Keys is new Sets.Generic_Keys (Key_Type => String,
|
||||
Key => Key,
|
||||
"<" => Standard."<");
|
||||
|
||||
Dict : Sets.Set;
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
function "<" (Left, Right : in Acc) return Boolean
|
||||
is (Left.all.Data < Right.all.Data);
|
||||
|
||||
procedure Adjust (Object : in out Ptr) is
|
||||
begin
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs + 1;
|
||||
end Adjust;
|
||||
|
||||
function Constructor (Source : in String) return Ptr is
|
||||
Position : constant Sets.Cursor := Keys.Find (Dict, Source);
|
||||
Ref : Acc;
|
||||
begin
|
||||
if Sets.Has_Element (Position) then
|
||||
Ref := Sets.Element (Position);
|
||||
Ref.all.Refs := Ref.all.Refs + 1;
|
||||
else
|
||||
Ref := new Rec'(Data => Source,
|
||||
Hash => Ada.Strings.Hash (Source),
|
||||
Last => Source'Length,
|
||||
Refs => 1);
|
||||
Dict.Insert (Ref);
|
||||
end if;
|
||||
return (Ada.Finalization.Controlled with Ref);
|
||||
end Constructor;
|
||||
|
||||
function Eq (Left, Right : in Acc) return Boolean is
|
||||
begin
|
||||
pragma Assert (Left /= Right);
|
||||
pragma Assert (Left.all.Data /= Right.all.Data);
|
||||
return False;
|
||||
end Eq;
|
||||
|
||||
procedure Finalize (Object : in out Ptr) is
|
||||
begin
|
||||
if Object.Ref /= null and then 0 < Object.Ref.all.Refs then
|
||||
Object.Ref.all.Refs := Object.Ref.all.Refs - 1;
|
||||
if 0 < Object.Ref.all.Refs then
|
||||
Object.Ref := null;
|
||||
else
|
||||
Dict.Delete (Object.Ref);
|
||||
Free (Object.Ref);
|
||||
end if;
|
||||
end if;
|
||||
end Finalize;
|
||||
|
||||
function Hash (Item : in Ptr) return Ada.Containers.Hash_Type
|
||||
is (Item.Ref.all.Hash);
|
||||
|
||||
function Key (Item : in Acc) return String
|
||||
is (Item.all.Data);
|
||||
|
||||
function To_String (Item : in Ptr) return String
|
||||
is (Item.Ref.all.Data);
|
||||
|
||||
end Types.Symbols;
|
59
ada2/types-symbols.ads
Normal file
59
ada2/types-symbols.ads
Normal file
@ -0,0 +1,59 @@
|
||||
with Ada.Containers;
|
||||
private with Ada.Finalization;
|
||||
|
||||
package Types.Symbols with Preelaborate is
|
||||
|
||||
type Ptr is tagged private;
|
||||
-- A wrapper for a pointer counting references.
|
||||
|
||||
-- The default value is invalid, new variables must be assigned
|
||||
-- immediately (a hidden discriminant would prevent this type to
|
||||
-- become a field inside Types.Mal.T, so we check this with a
|
||||
-- private invariant a fallback, an invariant in the private part
|
||||
-- checks that any created object is affected before use.
|
||||
|
||||
-- Assignment give another reference to the same storage.
|
||||
|
||||
function Constructor (Source : in String) return Ptr with Inline;
|
||||
-- The only way to assign a valid value.
|
||||
|
||||
function To_String (Item : in Ptr) return String with Inline;
|
||||
|
||||
-- The hash value is made available because symbols have a high
|
||||
-- probability to end up as keys in an environment.
|
||||
function Hash (Item : in Ptr) return Ada.Containers.Hash_Type with Inline;
|
||||
|
||||
-- Equality compares the contents.
|
||||
|
||||
private
|
||||
|
||||
-- Only one instance is allocated with a given content. This
|
||||
-- avoids many allocations and deallocations, since symbols are
|
||||
-- expected to be used many times.
|
||||
|
||||
-- Tests seem to show that this solution is a few percents faster
|
||||
-- than Ada.Strings.Unbounded.
|
||||
|
||||
-- As a side effect, some frequent string comparisons (with "def!"
|
||||
-- or "fn*" for example) will become a bit more efficient because
|
||||
-- comparing pointers is faster than comparing strings.
|
||||
|
||||
-- It would be natural to store a Cursor from the global
|
||||
-- dictionnary into Ptr, but this actually reduces the speed,
|
||||
-- probably because it significantly increases the size of
|
||||
-- Mal_Type.
|
||||
|
||||
-- See README for the implementation of reference counting.
|
||||
|
||||
type Rec;
|
||||
type Acc is access Rec;
|
||||
type Ptr is new Ada.Finalization.Controlled with record
|
||||
Ref : Acc := null;
|
||||
end record
|
||||
with Invariant => Ref /= null;
|
||||
overriding procedure Adjust (Object : in out Ptr) with Inline;
|
||||
overriding procedure Finalize (Object : in out Ptr) with Inline;
|
||||
-- Predefined equality is fine.
|
||||
pragma Finalize_Storage_Only (Ptr);
|
||||
|
||||
end Types.Symbols;
|
@ -1,37 +0,0 @@
|
||||
package body Types is
|
||||
|
||||
function "=" (Left, Right : in Mal_Type) return Boolean is
|
||||
(case Left.Kind is
|
||||
when Kind_Nil =>
|
||||
Right.Kind = Kind_Nil,
|
||||
when Kind_Atom =>
|
||||
Right.Kind = Kind_Atom
|
||||
and then Atoms."=" (Left.Reference, Right.Reference),
|
||||
when Kind_Boolean =>
|
||||
Right.Kind = Kind_Boolean
|
||||
and then Left.Boolean_Value = Right.Boolean_Value,
|
||||
when Kind_Number =>
|
||||
Right.Kind = Kind_Number
|
||||
and then Left.Integer_Value = Right.Integer_Value,
|
||||
when Kind_String | Kind_Keyword | Kind_Symbol =>
|
||||
Right.Kind = Left.Kind
|
||||
and then Strings."=" (Left.S, Right.S),
|
||||
when Kind_List | Kind_Vector =>
|
||||
Right.Kind in Kind_List | Kind_Vector
|
||||
and then Lists."=" (Left.L, Right.L),
|
||||
when Kind_Map =>
|
||||
Right.Kind = Kind_Map
|
||||
and then Maps."=" (Left.Map, Right.Map),
|
||||
when Kind_Function =>
|
||||
Right.Kind = Kind_Function
|
||||
and then Lists."=" (Left.Formals, Right.Formals)
|
||||
and then Atoms."=" (Left.Expression, Right.Expression)
|
||||
and then Environments."=" (Left.Environment, Right.Environment),
|
||||
when Kind_Native =>
|
||||
Right.Kind = Kind_Native and then Left.Native = Right.Native,
|
||||
when Kind_Macro =>
|
||||
Right.Kind = Kind_Macro
|
||||
and then Atoms."=" (Left.Mac_Expression, Right.Mac_Expression)
|
||||
and then Lists."=" (Left.Mac_Formals, Right.Mac_Formals));
|
||||
|
||||
end Types;
|
@ -1,17 +1,7 @@
|
||||
with Atoms;
|
||||
with Environments;
|
||||
with Lists;
|
||||
with Maps;
|
||||
with Strings;
|
||||
package Types with Pure is
|
||||
|
||||
package Types is
|
||||
|
||||
type Mal_Type;
|
||||
type Mal_Type_Array;
|
||||
type Native_Function_Access is not null access
|
||||
function (Arguments : in Mal_Type_Array) return Mal_Type;
|
||||
|
||||
-- Make similar kinds consecutive for efficient case statements.
|
||||
-- Similar kinds should be consecutive for efficient case
|
||||
-- statements.
|
||||
type Kind_Type is
|
||||
(Kind_Nil,
|
||||
Kind_Atom,
|
||||
@ -20,40 +10,11 @@ package Types is
|
||||
Kind_String, Kind_Symbol, Kind_Keyword,
|
||||
Kind_List, Kind_Vector,
|
||||
Kind_Map,
|
||||
Kind_Macro, Kind_Function, Kind_Native);
|
||||
Kind_Macro, Kind_Function, Kind_Builtin_With_Meta, Kind_Builtin);
|
||||
|
||||
type Mal_Type (Kind : Kind_Type := Kind_Nil) is record
|
||||
Meta : Atoms.Ptr;
|
||||
case Kind is
|
||||
when Kind_Nil =>
|
||||
null;
|
||||
when Kind_Boolean =>
|
||||
Boolean_Value : Boolean;
|
||||
when Kind_Number =>
|
||||
Integer_Value : Integer;
|
||||
when Kind_Atom =>
|
||||
Reference : Atoms.Ptr;
|
||||
when Kind_String | Kind_Keyword | Kind_Symbol =>
|
||||
S : Strings.Ptr;
|
||||
when Kind_List | Kind_Vector =>
|
||||
L : Lists.Ptr;
|
||||
when Kind_Map =>
|
||||
Map : Maps.Ptr;
|
||||
when Kind_Native =>
|
||||
Native : Native_Function_Access;
|
||||
when Kind_Function =>
|
||||
Formals : Lists.Ptr;
|
||||
Expression : Atoms.Ptr;
|
||||
Environment : Environments.Ptr;
|
||||
when Kind_Macro =>
|
||||
Mac_Formals : Lists.Ptr;
|
||||
Mac_Expression : Atoms.Ptr;
|
||||
end case;
|
||||
end record;
|
||||
|
||||
function "=" (Left, Right : in Mal_Type) return Boolean;
|
||||
-- By default, a list /= a vector.
|
||||
|
||||
type Mal_Type_Array is array (Positive range <>) of Types.Mal_Type;
|
||||
-- Raised when a program attempts to execute something else than a
|
||||
-- function or a macro, or when a builtin receives a bad argument
|
||||
-- count, type or value.
|
||||
Argument_Error : exception;
|
||||
|
||||
end Types;
|
||||
|
Loading…
Reference in New Issue
Block a user