Reputation: 4957
I'm trying to write a lexer in Ada and have run into an issue.
procedure main is
...
type lexer is tagged record
input : ada.text_io.file_type;
index : integer;
end record;
...
my_lexer : lexer;
input_file_name : bounded_string;
input_file : ada.text_io.file_type;
next_token : token;
begin
input_file_name := get_input_file;
ada.text_io.open(
file => input_file,
mode => ada.text_io.in_file,
name => to_string(input_file_name)
);
my_lexer := (input => input_file, index => 0);
next_token := my_lexer.get_next_token;
ada.text_io.put_line(to_string(next_token.text));
end main;
On the line my_lexer := (input => input_file);
I get the following error:
main.adb:22:17: nonlimited tagged type cannot have limited components
I understand this to be an issue since ada.text_io.in_file
is a limited type, but if I were to just remove this from the record, and instead pass it as an argument to get_next_token
,
...
type lexer is tagged record
index : integer;
end record;
function get_next_token(this: lexer'class; input_file: ada.text_io.file_type) return token;
...
Then this would make the state of the lexer invalid if you were to switch the input_file
between calls of get_next_token
thus breaking it.
Is there a way in Ada to create something like this like you would in C?
struct lexer {
FILE *input;
int index;
};
Upvotes: 1
Views: 296
Reputation: 3358
What I'd do is hide all these details and create an abstraction:
package Lexing is
type Info is tagged limited private;
function Is_Open (State : in Info) return Boolean;
-- Description of subprogram here
procedure Open (State : in out Info; Name : in String) with
Pre => not State_Is_Open,
Post => State.Is_Open;
-- Description of subprogram here
procedure Close (State : in out Info) with
Pre => State.Is_Open,
Post => not State.Is_Open;
-- Description of subprogram here
type Token_Info is tagged private;
function Next (State : in out Info) return Token_Info with
Pre => State.Is_Open;
-- Description of subprogram here
function Text (Token : in Token_Info) return String;
-- Description of subprogram here
...
private -- Lexing
...
end Lexing;
Obviously the fun bits are left as an exercise for the reader.
Upvotes: 2
Reputation: 61
Usually when you run into problems like this there are two solutions
limited
type and use it in your record, orlimited
. This is maybe (in my opinion) the cleaner solution since you do not have to mess with allocation, release, and stuff. (Personally, I try to avoid access types as much as possible). Of course, making it limited will prevent you to assign it, but maybe this is not a major problem in the case of a lexer.Upvotes: 3
Reputation:
One approach, allowing the lexer
type to be tagged limited
is to create the limited object in-place, so that its components (specifically the limited file_type
component) aren't created elsewhere (legal) and then assigned (copied, illegal). In this case, the file
component of lexer
is passed to the Open
call, which updates it in place.
procedure lex2 is
type lexer is tagged limited record
input : ada.text_io.file_type;
index : integer;
end record;
input_file_name : unbounded_string;
my_lexer : lexer;
-- next_token : token;
begin
input_file_name := get_input_file;
my_lexer.index := 0;
ada.text_io.open( file => my_lexer.input,
mode => ada.text_io.in_file,
name => to_string(input_file_name));
-- next_token := my_lexer.get_next_token;
-- ada.text_io.put_line(to_string(next_token.text));
end lex2;
Upvotes: 3
Reputation: 4957
I'm not sure if this is the best solution, but this is what I've come up with:
procedure main is
type file_access is access all ada.text_io.file_type;
...
type lexer is tagged record
input : file_access;
index : integer;
end record;
...
my_lexer : lexer;
input_file_name : bounded_string;
input_file : aliased ada.text_io.file_type;
next_token : token;
begin
input_file_name := get_input_file;
ada.text_io.open(
file => input_file,
mode => ada.text_io.in_file,
name => to_string(input_file_name)
);
my_lexer := (input => input_file'access, index => 0);
next_token := my_lexer.get_next_token;
ada.text_io.put_line(to_string(next_token.text));
end main;
Seems a bit convoluted, but I believe it's a way to ensure an access of input_file
lives for as long as the procedure.
Upvotes: 1