AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Einheiten parsen

Ein Thema von Bjoerk · begonnen am 9. Mär 2015 · letzter Beitrag vom 14. Mär 2015
Antwort Antwort
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#1

AW: Einheiten parsen

  Alt 10. Mär 2015, 17:01
Das Parsen gestaltet sich jetzt doch schwieriger als angenommen.

Der Term sieht intern so aus. In eckigen Klammern die Einheiten. Die Einheiten werden durch einfache Token ersetzt.

5,0[D]*0,70[K]/cos(30)

Das das soll jetzt hier durchgejagt werden. Hab keinen Plan wie ich das hier anstellen soll? (Getrenntes Parsen von Einheitn und Term ist m.E. aus syntaktischen Gründen nicht möglich.)

Im Beispiel landet der Parser irgendwann in GetPlus. Er muß aber auch mal irengdwann in FUnit.GetPlus landen (Der Parser hat ein Feld der Klasse TUntis (Siehe #18)).

Delphi-Quellcode:
// Die eigentliche Auflösung des Terms;
// *** Die Rekursion arbeitet rückwärts;
// "~" (unäres Minus) immer zuerst prüfen ;
// Sqrt vor Sqr, ArcXXX vor XXX;
// Entweder LastPos und true oder FirstPos und false;
// LastPos und true: "+", "-", "*", "/", ":", "\", (":" = div, "\" = mod);
// FirstPos und false: "^", Functions;
function TJMUnitParser.Solve(S: string; var Style: TUnitStyle): double;
begin
  try
    if LastPos('+', S) > 0 then
      Result := GetPlus(Solve(Left(S, '+', true), Style), Solve(Right(S, '+', true), Style))
    else
      ... Weitere ..
    else
    if Parenthesis(S, true) then // "~( .. )"
      Result := -Solve(S, Style)
    else
    if Parenthesis(S, false) then // "( .. )"
      Result := Solve(S, Style)
    else
      Result := StrToFloat(MyStringReplace(S, '~', '-', false, false));
  except
    Result := 0;
  end;
end;

// Gibt den linken Teil einer Operation zurück;
function TJMUnitParser.Left(const S, Substr: string; const Last: boolean): string;
begin
  if Last then // Den Term von rechts nach links durchsuchen;
    Result := StrLeft(S, LastPos(Substr, S) - 1)
  else // Den Term von links nach rechts durchsuchen;
    Result := StrLeft(S, FirstPos(Substr, S) - 1);
 end;

// Gibt den rechten Teil einer Operation zurück;
function TJMUnitParser.Right(const S, Substr: string; const Last: boolean): string;
begin
  if Last then // Den Term von rechts nach links durchsuchen;
    Result := StrRight(S, LastPos(Substr, S) + Length(Substr))
  else // Den Term von links nach rechts durchsuchen;
    Result := StrRight(S, FirstPos(Substr, S) + Length(Substr));
end;

function TJMUnitParser.GetPlus(const X, Y: double): double;
begin
  Result := X + Y;
end;
  Mit Zitat antworten Zitat
Mikkey

Registriert seit: 5. Aug 2013
265 Beiträge
 
#2

AW: Einheiten parsen

  Alt 10. Mär 2015, 17:15
Ich würde es für sinnvoller und wesentlich einfacher halten, wenn Du Dir einen fertigen Parser nimmst, und diesen entsprechend nach Deinen Bedürfnissen anpasst.

- Leerzeichen werden ignoriert
- Du hast Zahlen, musst das Dezimalkomma ensprechend behandeln.
- wenn auf eine Ziffer ein Buchstabe folgt, handelt es sich um eine Einheit, implizit wird eine Multiplikation eingefügt; die Einheit wird gegen den Satz von Einheiten geprüft
- wenn auf einen Buchstaben eine Klammer auf folgt, ist es ein Funktionsaufruf, das Token wird dann gegen den Satz der verfügbaren Funktionen geprüft
- Klammerung sollte der Standardparser bereits beherrschen.

Es ist generell keine gute Idee, in den Parser gleich die Interpretation des Ausdrucks einzubauen. Stattdessen kannst Du den gepars-ten Ausdruck auswerten und weißt dabei vorher, dass Du keinen Syntaxfehler hast.
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: Einheiten parsen

  Alt 10. Mär 2015, 18:21
Hallo Mikkey, das ist ein fertiger Parser. Und implizit kann man da m.E. nicht einfach mal eine Multiplikation einführen? Mir gehts hier nur um das Handling der Rekursion (Wie kommt man die Einheit ran?)
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#4

AW: Einheiten parsen

  Alt 11. Mär 2015, 04:39
Eine Konstante ist nicht nur einfach eine Zahl, sondern entweder ein Faktor oder ein physikalischer Wert.

Code:
NumericConstant ::= Factor | PhysicalValue
Factor ::= AnyNumber
PhysicalValue ::= AnyNumber [Unit]
PS: Wenn du die Frage nach Rekursivität stellst, dann hast Du nicht wirklich einen Parser. Oder Du hast einen, weißt aber nicht, wie man ihn verwendet. Oder Du hast einen, weißt, wie man ihn verwendet, hast aber einfach die falsche Frage gestellt.

Geändert von Dejan Vu (11. Mär 2015 um 04:43 Uhr)
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: Einheiten parsen

  Alt 11. Mär 2015, 15:15
Hi Bud,
ja, die Rekursion hatte ich gestern nicht wirklich (mehr) kapiert gehabt. Aber heute ist ein neuer Tag. So wie ich's vorhatte ging's aus mehreren Gründen nicht. Man kann entweder mit Äpfeln (double) mit oder Birnen (TEinheiten) rechnen. Also dann doch einen Extradurchlauf. Man braucht eine neue Solve.
Delphi-Quellcode:
function TJMUnitParser.Solve(S: string): TParserUnitStyle;
begin
  try
    if LastPos('+', S) > 0 then
      Result := GetPlus(Solve(Left(S, '+', true)), Solve(Right(S, '+', true)))
    else
      if LastPos('-', S) > 0 then
        Result := GetMinus(Solve(Left(S, '-', true)), Solve(Right(S, '-', true)))
      else
        if LastPos('*', S) > 0 then
          Result := GetMult(Solve(Left(S, '*', true)), Solve(Right(S, '*', true)))
        else
          if LastPos('/', S) > 0 then
            Result := GetDiv(Solve(Left(S, '/', true)), Solve(Right(S, '/', true)))
          else
            if LastPos(':', S) > 0 then // div;
              Result := GetIntDiv(Solve(Left(S, ':', true)), Solve(Right(S, ':', true)))
            else
              if LastPos('\', S) > 0 then // mod;
                Result := GetIntMod(Solve(Left(S, '\', true)), Solve(Right(S, '\', true)))
              else
                if FirstPos('^', S) > 0 then
                  Result := GetPower(Solve(Left(S, '^', false)), Solve(Right(S, '^', false)))
                else
                  if Parenthesis(S) then // ( .. )
                    Result := Solve(S)
                  else
                    Result := FUnits.SignToStyle(S);
  except
    Result := pusNone;
  end;
end;
Vom Ablauf her isses hier so: Im obigen Beispiel 5,0 kN/m2 * 0,70 m / cos(30) wird beim Aufbereiten für's Einheitenparsen zunächst [Einheit_1][Einheit_ kN/m2] * [Einheit_m] / [Einheit_1]. Um den Term Term mathematisch so zu parsen wie den Term ohne Einheiten braucht muß man nur [Einheit_1][Einheit_ X] zu [Einheit_ X] ersetzen (bzw. [Einheit_ X][Einheit_1] zu [Einheit_ X]).

Hab den Code mal angehängt. Falls du Böcke hat, eine Variante mit einem Stack wär' nicht schlecht.

LG
Thomas
Angehängte Dateien
Dateityp: zip JMParser.zip (6,2 KB, 3x aufgerufen)
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#6

AW: Einheiten parsen

  Alt 12. Mär 2015, 06:15
Hmm. Du solltest deinen Parser so schreiben, wie man eine Grammatik schreibt.
D.h.
1. Du baust dir einen Tokenizer/Lexer, der dir aus deinem String die entsprechenden Token (Operator, Klammer, Zahl, Einheit) extrahiert.

2. Dann schreibst Du den Parser. Leider hab ich gerade keinen da, weil ich unterwegs bin und mein Privatlaptop mit einem fertigen Parser zuhause ist. Im Prinzip geht es so:

Delphi-Quellcode:
// Term ::= Zahl Operator Zahl | '(' Term ')'
function IsTerm (var term : TParserNode) : Boolean;
Var
  leftNumber, operator, rightNumber : TToken;

begin
  result := false
  if IsOpenBracket then begin
    if IsTerm (term) then
      if IsClosingBracket then
        exit(true);
    SyntaxError('Term expected');
  end
  else
    if IsZahl(leftNumber) then
      if IsOperator(operator) then
        If IsZahl(rightNumber) then begin
          term := TTermNode.Create (leftNumber, operator, rightNumber)
          result := True;
        end;
// falls result=false ist, müsstest du hier noch aufräumen, oder mit interfaces arbeiten
end;
'IsZahl' liefert true, wenn das nächste Token eine Zahl ist. außerdem wird in 'leftNumber' dann die Zahl geliefert.
Das gleiche gilt für 'IsOperator'. 'IsTerm' macht im Prinzip genau das Gleiche: Wenn es die Tokensequenz einen Term ergibt, liefert die Funktion 'True' und den Term.

Wichtig ist, das man beim Erkennen eines Tokens zum nächsten geht. Manchmal muss man sich das aktuelle Token, sondern das darauf folgende Token anschauen, um zu entscheiden, was zu tun ist (look ahead).
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: Einheiten parsen

  Alt 12. Mär 2015, 06:52
Ja, das weiß ich doch selbst? Dieser Parser arbeitet aber halt anders. Ansonsten ja zum Beispiel so, so einen hab ich auch mal gemacht.
Delphi-Quellcode:
procedure TParser.ParseFunctions(var S: string);
var
  Index: integer;
begin
  Index := OperatorPos(S, 'A');
  while Index > 0 do
  begin
    MathWork(S, Index);
    Index := OperatorPos(S, 'A');
  end;
end;

procedure TParser.ParseOperator(var S: string; const AOperator: Char);
var
  Index: integer;
begin
  Index := OperatorPos(S, AOperator);
  while Index > 0 do
  begin
    MathWork(S, Index);
    Index := OperatorPos(S, AOperator);
  end;
end;

procedure TParser.Parse(var S: string);
begin
  // Diese Reihenfolge
  ParseFunctions(S); // Funktionen, Von Rechts nach Links;
  ParseOperator(S, '^'); // Power, Von Rechts nach Links;
  ParseOperator(S, '*'); // Punktrechnung, Von Links nach Rechts;
  ParseOperator(S, '+'); // Strichrechnung Von Links nach Rechts;
end;

function TParser.Solve(T: string; Sender: boolean): string; // Aufruf mit Sender = false;
var
  A, B: Integer;
  S: string;
  AFloat: double;
begin
  while HaveBrackets(T, A, B) do
  begin
    S := StrMid(T, A + 1, B - 1);
    Solve(S, true);
    Parse(S);
    T := StrLeft(T, A - 1) + S + StrRight(T, B + 1);
  end;
  if not Sender then
  begin
    Parse(T);
    if TryStrToFloat(GetNumber(T), AFloat) then
      Result := GetNumber(T)
    else
      Result := '0'; // Syntaxerror
  end;
end;
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:56 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz