скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

Пример 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






Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook