使用 File_Type 作为记录组件?

Using File_Type as a record component?

我正在尝试用 Ada 编写词法分析器,但遇到了 运行 问题。

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;

在线my_lexer := (input => input_file);我收到以下错误:

main.adb:22:17: nonlimited tagged type cannot have limited components

我知道这是一个问题,因为 ada.text_io.in_file 是一个受限类型,但如果我只是将其从记录中删除,而是将其作为参数传递给 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;
    ...

然后,如果您要在 get_next_token 的调用之间切换 input_file 从而破坏它,这将使词法分析器的状态无效。

有没有办法在 Ada 中像在 C 中那样创建类似的东西?

struct lexer {
    FILE *input;
    int index;
};

允许 lexer 类型为 tagged limited 的一种方法是就地创建受限对象,以便其组件(特别是受限 file_type 组件)' t 在别处创建(合法)然后分配(复制,非法)。在这种情况下,lexerfile 组件被传递给 Open 调用,该调用就地更新它。

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;

我不确定这是否是最佳解决方案,但这是我想出的:

 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;

似乎有点令人费解,但我相信这是一种确保 input_file 的访问与过程一样长的方法。

我要做的是隐藏所有这些细节并创建一个抽象:

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;

显然,有趣的部分留作 reader 的练习。

通常当你运行遇到这样的问题时,有两种解决方案

  1. 定义一个新类型,它是对 limited 类型的访问,并在您的记录中使用它,或者
  2. 打字limited。这可能(在我看来)是更清洁的解决方案,因为您不必处理分配、释放和其他事情。 (就个人而言,我尽量避免访问类型)。当然,限制它会阻止你分配它,但在词法分析器的情况下这可能不是主要问题。