2

我是 Ada 的新手,我正在尝试将 Recursive Decent Parser 的编译器教程中的代码转换为 Ada。移植 Jack W Crenshaw 的教程“让我们构建编译器”一直是我学习多种语言的最喜欢的方式。在第三章之前,我使用单字符标记完成了所有工作。向多字符令牌的转变一直很麻烦。

我有类似 sudo 代码的代码:

procedure GetName is
  token: Ada.Strings.Unbounded;
begin
  while IsAlNum(Look) loop
    Token := Token & Look;
    GetChar;
  end loop
end GetName;

现在我知道 Ada 打算让字符串保持静态。但我需要能够将从输入中获取的每个新字符连接到 Token 中的字符集合。Look 是全局预测值(最后输入的字符)。

谢谢你的帮助。另外,网上有什么好的 Ada 教程或食谱网站吗?我为 C 程序员阅读了 Lovelace 和 Ada。Ada RM 有点正式,只显示规格不使用...

再次感谢!

4

4 回答 4

3

Ada 的单字符“get”方法是Ada.Text_IO.Get。在 Text_IO 包的该部分中还有 Look_Ahead 和 Get_Immediate 过程。

Rosetta Code 的Ada Category是很多 Ada 示例的一个很好的来源。

于 2012-06-11T00:35:17.907 回答
3

如果您要使用 Ada 2005 或更高版本(实际上可能有 95,但不完全确定),您可以使用 Streams。像这样的东西:

With Ada.Text_IO;

With
Ada.Streams.Stream_IO,
Ada.Text_IO.Text_Streams,
Ada.IO_Exceptions;


Procedure IO is
    Use Ada.Text_IO;

    -- Get the Standard_Input.
    Input_File : Ada.Text_IO.File_Type:= Ada.Text_IO.Standard_Input;

    -- Create a stream from the Standard Input.
    Input_Stream : Access Ada.Streams.Root_Stream_Type'Class:=
      Ada.Text_IO.Text_Streams.Stream( File => Input_File );

Begin

    GET_USER_INPUT:
    declare
    C: Character;
    begin
    loop
        Character'Read( Input_Stream, C );
        exit when C = '*';
        -- Build your string here.
    end loop;
    -- THIS IS AN ALTERNATE WAY FOR EXITING THE ABOVE LOOP.
    Exception
    When ADA.IO_EXCEPTIONS.END_ERROR => Null; -- Raised normally at EOF.
    end GET_USER_INPUT;
    -- Suggested, refactoring GET_USER_INPUT into a function.

    Put_Line( "Testing." );

End IO;
于 2012-06-11T23:43:16.123 回答
3

在这个问题的最后,您似乎在寻求 Ada 字符串处理方面的帮助。

是的,Ada 字符串确实最好作为静态字符串处理,而不是可调整大小的缓冲区。有三种典型的方法来处理这个问题。

第一个是创建一个非常大的String缓冲区,用一个单独的Natural变量来保存字符串的逻辑长度。这有点痛苦,而且有点容易出错,但至少比 C 中在缓冲区末尾不断扫描空值的方法快。

第二种是直接使用 Ada.Strings.Unbounded.Unbounded_String。这是大多数人所做的,因为如果您习惯于以程序方式思考事物,那将是最简单的。

第三,(如果可能的话,我更喜欢)是在功能上处理你的字符串。您在这里需要的主要见解是 AdaString确实是静态的,但您可以控制它们的生命周期,并且如果您进行函数式编程,您可以随时动态地制作静态字符串。

例如,我可以Token通过执行以下操作来创建一个我想要的任何长度的新字符串(理论上无限前瞻):

function Matches_Token (Scanned : String) return boolean;  --// Returns true if the given string is a token
function Could_Match_Longer (Scanned : String) return boolean; --// Returns true if the given string could be part of a larger token.
function Get_Next_Char return Character;  --// Returns the next character from the stream
procedure Unget; --// Puts the last character back onto the stream
procedure Advance (Amount : Natural); --// Advance the stream pointer the given amount
function Longest_Matching_Token (Scanned : String) return String is
    New_Token : constant String := Scanned & Get_Next_Char;
begin
    --// Find the longest token a further scan can match
    if Could_Match_Longer(New_Token) then
        declare 
            LMT : constant String := Longest_Matching_Token (New_Token);
        begin
            if LMT /= "" then
                unget;
                return LMT;
            end if;
        end;
    end if;

    --// See if this string at least matches.
    if Matches_Token(New_Token) then
        unget;
        return New_Token;
    else
        unget;
        return "";
    end if;
end Build_Token;

function Get_Next_Token return String is
    Next_Token : constant String := Build_Token("");
begin
    Advance (Next_Token'length);
    return Next_Token;
end Get_Next_Token;

这并不总是最有效的字符串处理方法(堆栈使用过多),但它通常是最简单的。

在实践中,扫描和解析实际上是一种特殊情况的应用程序,通常建议避免丑陋的东西,例如缓冲区(方法 1)和 goto。

于 2012-06-13T20:40:30.553 回答
2

我编写了一个函数来连接从输入中获取的每个新字符,并将其全部作为字符串返回。它可能适合做你需要的事情。

FUNCTION get_a_string ( ch : IN Character ) RETURN String IS
-----------------------------------------------------------------
--| Recursively hack out a string from a stream of single
--| character input. Starting with an ESC sentinel and ending
--| with an EOL sentinel.
--| ESC is the ESC character and EOL is a space.
----------------------------------------------------------------
    next : Character;
    ch2s : String(1..1);
BEGIN -- get_a_string
    Ada.Text_IO.Put( "Waiting: ");--BARF
    Ada.Text_IO.Get( Item => next );

    IF ch = ESC THEN -- start
       RETURN get_a_string(next);
    ELSIF next = EOL THEN --Escape Case
       ch2s(1) := ch;
       RETURN ch2s;
    ELSE -- Keep getting input
       RETURN ch & get_a_string(next);
    END IF;
END get_a_string;

万一我的教练或助教发现这与我上交的代码匹配。我写了它,所以我没有作弊。

于 2012-10-17T22:56:33.763 回答