1 with Ada
.IO_Exceptions
;
2 with Ada
.Characters
.Latin_1
;
4 with Ada
.Strings
.Maps
.Constants
;
5 with Ada
.Strings
.Unbounded
;
11 package body Reader
is
15 package ACL
renames Ada
.Characters
.Latin_1
;
17 type Lexemes
is (Ignored_Tok
,
18 Start_List_Tok
, Start_Vector_Tok
, Start_Hash_Tok
,
20 Quote_Tok
, Quasi_Quote_Tok
, Splice_Unq_Tok
, Unquote_Tok
,
24 type Token
(ID
: Lexemes
:= Ignored_Tok
) is record
27 Int_Val
: Mal_Integer
;
29 Float_Val
: Mal_Float
;
30 when Str_Tok | Sym_Tok
=>
31 Start_Char
, Stop_Char
: Natural;
36 Lisp_Whitespace
: constant Ada
.Strings
.Maps
.Character_Set
:=
37 Ada
.Strings
.Maps
.To_Set
38 (ACL
.HT
& ACL
.LF
& ACL
.CR
& ACL
.Space
& ACL
.Comma
);
41 Terminator_Syms
: Ada
.Strings
.Maps
.Character_Set
:=
44 Ada
.Strings
.Maps
.To_Set
("[]{}('""`,;)"));
46 -- The unterminated string error
47 String_Error
: exception;
50 function Convert_String
(S
: String) return String is
51 use Ada
.Strings
.Unbounded
;
52 Res
: Unbounded_String
;
58 while I
<= Str_Last
loop
60 if I
+1 > Str_Last
then
63 elsif S
(I
+1) = 'n' then
64 Append
(Res
, Ada
.Characters
.Latin_1
.LF
);
66 elsif S
(I
+1) = '"' then
67 Append
(Res
, S
(I
+1));
69 elsif S
(I
+1) = '\' then
70 Append
(Res
, S
(I
+1));
81 return To_String
(Res
);
84 Str_Len
: Natural := 0;
85 Saved_Line
: Ada
.Strings
.Unbounded
.Unbounded_String
;
86 Char_To_Read
: Natural := 1;
88 function Get_Token
return Token
is
91 use Ada
.Strings
.Unbounded
;
96 -- Skip over whitespace...
98 while I
<= Str_Len
and then
99 Ada
.Strings
.Maps
.Is_In
(Element
(Saved_Line
, I
), Lisp_Whitespace
) loop
103 -- Filter out lines consisting of only whitespace
105 return (ID
=> Ignored_Tok
);
110 case Element
(Saved_Line
, J
) is
112 when ''' => Res
:= (ID
=> Quote_Tok
); Char_To_Read
:= J
+1;
114 when '`' => Res
:= (ID
=> Quasi_Quote_Tok
); Char_To_Read
:= J
+1;
118 if J
+1 <= Str_Len
and then Element
(Saved_Line
, J
+1) = '@' then
119 Res
:= (ID
=> Splice_Unq_Tok
);
123 Res
:= (ID
=> Unquote_Tok
);
127 when '(' => Res
:= (ID
=> Start_List_Tok
); Char_To_Read
:= J
+1;
128 when '[' => Res
:= (ID
=> Start_Vector_Tok
); Char_To_Read
:= J
+1;
129 when '{' => Res
:= (ID
=> Start_Hash_Tok
); Char_To_Read
:= J
+1;
131 when '^' => Res
:= (ID
=> Meta_Tok
); Char_To_Read
:= J
+1;
132 when '@' => Res
:= (ID
=> Deref_Tok
); Char_To_Read
:= J
+1;
134 when ']' |
'}' |
')' =>
136 Res
:= (ID
=> Sym_Tok
, Start_Char
=> J
, Stop_Char
=> J
);
139 when '"' => -- a string
143 while J
<= Str_Len
and then
144 (Element
(Saved_Line
, J
) /= '"' or else
145 Element
(Saved_Line
, J
-1) = '\') loop
149 -- So we either ran out of string..
154 -- or we reached an unescaped "
155 Res
:= (ID
=> Str_Tok
, Start_Char
=> I
, Stop_Char
=> J
);
156 Char_To_Read
:= J
+ 1;
158 when ';' => -- a comment
160 -- Read to the end of the line or until
161 -- the saved_line string is exhausted.
162 -- NB if we reach the end we don't care
163 -- what the last char was.
164 while J
< Str_Len
and Element
(Saved_Line
, J
) /= ACL
.LF
loop
168 Res
:= (ID
=> Ignored_Tok
);
170 Char_To_Read
:= J
+ 1;
171 -- was: Res := Get_Token;
175 when others => -- an atom
177 while J
<= Str_Len
and then
178 not Ada
.Strings
.Maps
.Is_In
(Element
(Saved_Line
, J
), Terminator_Syms
) loop
182 -- Either we ran out of string or
183 -- the one at J was the start of a new token
189 All_Digits
: Boolean;
191 -- check if all digits or .
195 if (K
= I
and K
/= J
) and then Element
(Saved_Line
, K
) = '-' then
197 elsif Element
(Saved_Line
, K
) = '.' then
199 elsif not (Element
(Saved_Line
, K
) in '0' .. '9') then
209 Int_Val
=> Mal_Integer
'Value (Slice
(Saved_Line
, I
, J
)));
213 Float_Val
=> Mal_Float
'Value (Slice
(Saved_Line
, I
, J
)));
215 Res
:= (ID
=> Sym_Tok
, Start_Char
=> I
, Stop_Char
=> J
);
218 Res
:= (ID
=> Sym_Tok
, Start_Char
=> I
, Stop_Char
=> J
);
230 function Read_List
(LT
: Types
.List_Types
)
231 return Types
.Mal_Handle
is
240 List_SP
: Mal_Handle
;
241 List_P
: List_Class_Ptr
;
242 Close
: String (1..1) := (1 => Types
.Closing
(LT
));
246 when List_List
=> List_SP
:= New_List_Mal_Type
(List_Type
=> LT
);
247 when Vector_List
=> List_SP
:= Vector
.New_Vector_Mal_Type
;
248 when Hashed_List
=> List_SP
:= Hash_Map
.New_Hash_Map_Mal_Type
;
251 -- Need to append to a variable so...
252 List_P
:= Deref_List_Class
(List_SP
);
256 if Is_Null
(MTA
) then
257 return New_Error_Mal_Type
(Str
=> "expected '" & Close
& "'");
260 exit when Deref
(MTA
).Sym_Type
= Sym
and then
261 Symbol_Mal_Type
(Deref
(MTA
).all).Get_Sym
= Close
;
263 Append
(List_P
.all, MTA
);
276 function Read_Form
return Types
.Mal_Handle
is
279 use Ada
.Strings
.Unbounded
;
286 when Ignored_Tok
=> return Smart_Pointers
.Null_Smart_Pointer
;
288 when Int_Tok
=> return New_Int_Mal_Type
(Tok
.Int_Val
);
290 when Float_Tok
=> return New_Float_Mal_Type
(Tok
.Float_Val
);
292 when Start_List_Tok
=> return Read_List
(List_List
);
294 when Start_Vector_Tok
=> return Read_List
(Vector_List
);
296 when Start_Hash_Tok
=> return Read_List
(Hashed_List
);
301 Meta
, Obj
: Mal_Handle
;
306 ((1 => New_Symbol_Mal_Type
("with-meta"),
314 ((1 => New_Symbol_Mal_Type
("deref"),
320 ((1 => New_Symbol_Mal_Type
("quote"),
323 when Quasi_Quote_Tok
=>
326 ((1 => New_Symbol_Mal_Type
("quasiquote"),
329 when Splice_Unq_Tok
=>
332 ((1 => New_Symbol_Mal_Type
("splice-unquote"),
338 ((1 => New_Symbol_Mal_Type
("unquote"),
343 -- +/-1 strips out the double quotes.
344 -- Convert_String converts backquoted charaters to raw format.
345 return New_String_Mal_Type
347 (Slice
(Saved_Line
, Tok
.Start_Char
+ 1, Tok
.Stop_Char
- 1)));
351 -- Mal interpreter is required to know about true, false and nil.
353 S
: String := Slice
(Saved_Line
, Tok
.Start_Char
, Tok
.Stop_Char
);
356 return New_Bool_Mal_Type
(True);
357 elsif S
= "false" then
358 return New_Bool_Mal_Type
(False);
360 return New_Nil_Mal_Type
;
362 return New_Symbol_Mal_Type
(S
);
371 procedure Lex_Init
(S
: String) is
374 Saved_Line
:= Ada
.Strings
.Unbounded
.To_Unbounded_String
(S
);
379 function Read_Str
(S
: String) return Types
.Mal_Handle
is
380 I
, Str_Len
: Natural := S
'Length;
389 return New_Error_Mal_Type
(Str
=> "expected '""'");