Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
обновлён 2 декабря!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

БОЛЬШОЙ FAQ ПО DELPHI



Пример TParser

Автор: Mike Scott (Mobius Ltd)

С интересом прочли ваш совет про TParser. Может вы смогли бы нам еще немного помочь советом? Нам необходимо вычислить математическое выражение с применением стандартных механизмов (приоритеты, порядок и т.д.). Это возможно с помощью TParser? Или придется все делать ручками?

На самом деле TParser не является синтаксическим анализатором, скорее это лексический анализатор, или сканер. Или, другими словами, входной поток признаков (tokens) в ASCII-коде. Вы довольно легко можете использовать эти "признаки" для парсирования выражения, используя простую рекурсию. Сделав это, вы можете произвести разбор вашего математического выражения практически любой сложности. TParser вам поможет, но рекурсивный анализатор придется создавать ручками.

...простой пример, который мы хотим парсировать:

(23.34 + 21.21) * 2.92 - 12.21 * sin (180) * -1

Вот пример синтаксического анализатора выражений, наследника TParser, который может разобрать вышеуказанное выражение. Он использует следующие определения для выражений:

       Expr   ::= Term + Expr | Term - Expr | Term
       Term   ::= Factor * Term | Factor / Term | Factor
       Factor ::= + Item | - Item | Item
       Item   ::= ( Expr ) | Fn( Expr ) | Number
       Fn     ::= Sin | Cos
       Number ::= floating point literal number (плавающая точка литерала числа)
 
 
Далее идет модуль и форма, показывающие как это можно использовать. Вы должны скопировать текст формы в окно редактора Delphi и сохранить как DFM-файл. Мои расчеты вашего выражения привели к результату 130.086 - это правильно?

Примечание: TParser имеет ошибку в подпрограмме парсирования плавающего числа. Любое сочетание символов с символами '+' или '-' воспринимается как часть плавающего числа, поскольку 1e+3 корректное выражение. Естественно, это должно быть правильным только в совокупности с символом 'e'. Поэтому вы должны убедиться, что перед символами '+' и '-' имеется хотя бы один пробел, как показано в вашем выражении. Вы можете это исправить (если у вас есть исходный код VCL), редактируя функцию TParser.NextToken.

Скопируйте поочередно три приведенных ниже файла и вставьте их в окно редактора Delphi. Самый простой способ - закройте все открытые проекты и создайте новый модуль. Выделите весь текст, сгенерированный Delphi и вставьте текст модуля ExpParse. Сохраните его под именем ExpParse.pas. Затем создайте другой модуль, перенесите в него EvalForm.pas и также сохраните. Снова закройте файл и создайте новый модуль. Вставьте в него EvalForm.dfm и сохраните, выбрав меню "Save as" и отметив в списке тип файла DFM. Затем создайте новый проект, удалите форму, созданную по умолчанию и добавьте файл EvalForm.pas.


 // *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
 
 unit ExpParse;
 
 interface
 
 uses Classes;
 
 { Набор парсируемых элементов определяется как подмножество
 выражений Delphi Object Pascal, подобно этому:
 
 Expr   ::= Term + Expr | Term - Expr | Term
 Term   ::= Factor * Term | Factor / Term | Factor
 Factor ::= + Item | - Item | Item
 Item   ::= ( Expr ) | Fn( Expr ) | Number
 Fn     ::= Sin | Cos | другое...
 Number ::= floating point literal number (плавающая точка литерала числа)
 }
 
 type
   TExpressionParser = class(TParser)
   protected
     function SkipToken(Value: char): boolean;
     function EvalItem: double; virtual;
     function EvalFactor: double; virtual;
     function EvalTerm: double; virtual;
   public
     function EvalExpr: double;
   end;
 
 implementation
 
 uses SysUtils;
 
 function TExpressionParser.SkipToken(Value: char): boolean;
 begin
   { возвращаем истину, если текущий признак Value,
   и если так, то получаем следующий признак }
   Result := Token = Value;
   if Result then
     NextToken;
 end;
 
 function TExpressionParser.EvalItem: double;
 
 var
   Expr: double;
   Fn: integer;
 
 begin
   case Token of
     toInteger: Result := TokenInt;
     toFloat: Result := TokenFloat;
     '(':
       begin
         NextToken;
         Result := EvalExpr;
         CheckToken(')');
       end;
     toSymbol:
       begin
         if CompareText(TokenString, 'SIN') = 0 then
           Fn := 1
         else if CompareText(TokenString, 'COS') = 0 then
           Fn := 2
         else
           raise EParserError.CreateFmt('Неизвестный элемент "%s"', [TokenString]
             );
 
         NextToken;
         CheckToken('(');
         NextToken;
         Expr := EvalExpr;
         CheckToken(')');
         case Fn of
           1: Result := SIN(Expr);
           2: Result := COS(Expr);
         end;
       end;
   else
     raise EParserError.CreateFmt('Неожидаемый символ "%s"', [Token]);
   end;
   NextToken;
 end;
 
 function TExpressionParser.EvalFactor: double;
 
 begin
   case Token of
     '+':
       begin
         NextToken;
         Result := EvalItem;
       end;
     '-':
       begin
         NextToken;
         Result := -EvalItem;
       end;
   else
     Result := EvalItem;
   end;
 end;
 
 function TExpressionParser.EvalTerm: double;
 var
   AToken: char;
 begin
   Result := EvalFactor;
   if SkipToken('*') then
     Result := Result * EvalTerm
   else if SkipToken('/') then
     Result := Result / EvalTerm;
 end;
 
 function TExpressionParser.EvalExpr: double;
 begin
   Result := EvalTerm;
   if SkipToken('+') then
     Result := Result + EvalExpr
   else if SkipToken('-') then
     Result := Result - EvalExpr;
 end;
 
 end.
 
 // *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
 
 unit EvalForm;
 
 interface
 
 uses
   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
   Forms, Dialogs, StdCtrls, ExpParse;
 
 type
   TForm1 = class(TForm)
     Edit1: TEdit;
     Label1: TLabel;
     Button1: TButton;
     Label2: TLabel;
     procedure Button1Click(Sender: TObject);
   private
     { Private declarations }
   public
     { Public declarations }
   end;
 
 var
   Form1: TForm1;
 
 implementation
 
 {$R *.DFM}
 
 procedure TForm1.Button1Click(Sender: TObject);
 var
   s: string;
   MemStream: TMemoryStream;
   ExpressionParser: TExpressionParser;
 begin
   { get the string to evaluate }
   s := Edit1.Text;
 
   { создаем поток для работы с памятью, содержащий текст -
   TParser может разбирать выражения из потока}
 
   MemStream := TMemoryStream.Create;
   try
     MemStream.SetSize(Length(s));
     MemStream.WriteBuffer(s[1], Length(s));
     MemStream.Position := 0;
 
     { создаем анализатор выражения, используя поток }
     ExpressionParser := TExpressionParser.Create(MemStream);
     try
       Label2.Caption := Format('Результат=%g', [ExpressionParser.EvalExpr]
         );
 
     finally
       ExpressionParser.Free;
     end;
   finally
     MemStream.Free;
   end;
 end;
 end.
 
 // *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
 
 object Form1: TForm1
   Left = 216
     Top = 102
     Width = 433
     Height = 300
     Caption = 'Form1'
     Font.Color = clWindowText
     Font.Height = -13
     Font.Name = 'System'
     Font.Style = []
     PixelsPerInch = 96
     TextHeight = 16
     object Label1: TLabel
     Left = 8
       Top = 8
       Width = 74
       Height = 16
       Caption = 'Выражение'
   end
   object Label2: TLabel
     Left = 120
       Top = 72
       Width = 297
       Height = 16
       AutoSize = False
       Caption = 'Результат='
   end
   object Edit1: TEdit
     Left = 8
       Top = 27
       Width = 409
       Height = 24
       TabOrder = 0
       Text = '(23.34 + 21.21) * 2.92 - 12.21 * sin (180) * -1'
   end
   object Button1: TButton
     Left = 8
       Top = 64
       Width = 89
       Height = 33
       Caption = 'Оценка'
       default = True
       TabOrder = 1
       OnClick = Button1Click
   end
 end
 




<< ВЕРНУТЬСЯ В ОГЛАВЛЕНИЕ



Материалы находятся на сайте https://exelab.ru/pro/



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS