home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / 1987 / 08 / calc / calc.pas next >
Encoding:
Pascal/Delphi Source File  |  1987-05-22  |  17.7 KB  |  407 lines

  1. (***************************************************************************)
  2. (*                                                                         *)
  3. (*                            CALC.PAS                                     *)
  4. (*    Berechnung von arithmetischen Ausdruecken in Pascal zur Laufzeit     *)
  5. (*                                                                         *)
  6. (***************************************************************************)
  7.  
  8. (* ----------------------------------------------------------------------- *)
  9. (*            Uebersetzen eines Ausdrucks von AOS nach UPN:                *)
  10. (* 'Expr' enthaelt den zu uebersetzenden Ausdruck                          *)
  11. (* 'VarTable' ist ein Zeiger auf die fuer den zu uebersetzenden Ausdruck   *)
  12. (* zu verwendente Variablen-Tabelle. Ist dieser '= nil', wird von 'Calc-   *)
  13. (* Expression implizit eine solche Tabelle erzeugt und fuer den Ausdruck   *)
  14. (* verwendet. Man beachte in diesem Zusammenhang auch 'CalcDecMod'!        *)
  15. (* 'ExprPtr' gibt den Zeiger auf die von 'CompileExpression' erzeugte UPN- *)
  16. (* Queue zurueck. Dieser ist '= nil', wenn ein fehlerhafter Ausdruck vor-  *)
  17. (* liegt (kein Calc-Prog. erzeugt!).                                       *)
  18. (* 'CalcResult' ist TRUE, wenn kein Problem auftrat - sonst FALSE!         *)
  19. (* ----------------------------------------------------------------------- *)
  20.  
  21. PROCEDURE CompileExpression (Expr: Calc_String;
  22.                              VAR VarTable: Calc_VarTab;
  23.                              VAR ExprPtr : Calc_Prog);
  24.  
  25. VAR VarTabFlag,
  26.     ParsError,
  27.     EndOfExpr  : BOOLEAN;
  28.     ch         : STRING[1];                (* akt. Zeichen aus String      *)
  29.     LastPos,
  30.     StrPos     : INTEGER;                  (* zaehlt String-Position mit   *)
  31.     TempIdent,
  32.     Ident      : Calc_String;              (* enth. aktuellen Bezeichner   *)
  33.     Symbol,                                (* akt. Symbol des Bezeichners  *)
  34.     LastSymbol : Calc_Symbols;             (* vorheriges Symbol            *)
  35.     Number     : Calc_Operand;             (* akt. Zahl/Index aus String   *)
  36.     ProgPtr    : Calc_Prog;
  37.  
  38.    (* -------------------------------------------------------------------- *)
  39.    (* Fehlerbehandlung! Die Anweisungen des IF-Zweiges koennen nach        *)
  40.    (* eigenen Beduerfnissen (z.B. der Programm-Umgebung) angepasst werden. *)
  41.  
  42.    PROCEDURE Error (ErrPos: INTEGER; ErrMsg: Calc_String);
  43.  
  44.    BEGIN
  45.       IF NOT ParsError THEN
  46.       BEGIN
  47.          WriteLn; WriteLn('*** Fehler in CompileExpression:');
  48.          WriteLn (' ',Expr); WriteLn (' ':ErrPos, '^'); WriteLn (ErrMsg, '!')
  49.       END;
  50.       ParsError := TRUE;  Symbol := Calc_Err
  51.    END;
  52.  
  53.    (* -------------------------------------------------------------------- *)
  54.    (*           naechsten Befehl an die UPN-Queue anhaengen:               *)
  55.    (* Dabei wird mittels 'HeapAvail' der noch freie Heap geprueft. Wegen   *)
  56.    (* des eventuellen Aufrufs der Error-Prozedur wird nicht nur Platz fuer *)
  57.    (* ein Queue-Element benoetigt - deshalb ein paar Bytes mehr, um eine   *)
  58.    (* Heap-Stack-Kollision zu vermeiden !                                  *)
  59.  
  60.    PROCEDURE Add_To_Queue (op: Calc_Symbols; x: Calc_Operand);
  61.  
  62.    VAR UPN_Entry: Calc_Prog;
  63.  
  64.    BEGIN
  65.       IF HeapAvail > 160.0 THEN     (* hier: noch 160 Bytes im Heap frei ? *)
  66.          BEGIN
  67.             New (UPN_Entry);
  68.             WITH UPN_Entry^ DO
  69.             BEGIN
  70.                NextInst := NIL;
  71.                Instruct := op;
  72.                CASE op OF
  73.                   Calc_Var  : VarIndex := Trunc(x);
  74.                   Calc_Const: Operand  := x
  75.                END
  76.             END;
  77.             ProgPtr^.NextInst := UPN_Entry;
  78.             ProgPtr           := ProgPtr^.NextInst
  79.          END
  80.       ELSE Error(1, 'nicht genuegend freier Speicher fuer den Ausdruck')
  81.    END;
  82.  
  83.    (* -------------------------------------------------------------------- *)
  84.    (*           naechstes Symbol aus der Zeichenkette holen:               *)
  85.  
  86.    PROCEDURE GetSymbol;
  87.  
  88.       (* ----------------------------------------------------------------- *)
  89.       (*           holt naechstes Zeichen aus der Zeichenkette:            *)
  90.  
  91.       PROCEDURE GetChar;
  92.  
  93.       BEGIN
  94.          ch := ' ';  StrPos := Succ(StrPos);
  95.          EndOfExpr := (StrPos > Length(Expr));
  96.          IF NOT EndOfExpr THEN ch := UpCase (Expr[StrPos])
  97.       END;
  98.  
  99.       (* ----------------------------------------------------------------- *)
  100.       (*        holt und wandelt Ziffernfolge aus der Zeichenkette:        *)
  101.       (* die Val-Prozedur von Turbo Pascal moechte in der Zeichenkette nur *)
  102.       (* gueltige Zeichen einer Real-Zahl haben, NumberEnd zeigt auf das   *)
  103.       (* erste ungueltige Zeichen! Deshalb folgendes:                      *)
  104.  
  105.       PROCEDURE GetNumber;
  106.  
  107.       VAR NumberStr: Calc_String;
  108.           NumberEnd: INTEGER;
  109.  
  110.       BEGIN
  111.          NumberStr := Copy (Expr, StrPos, 255); (* nimm alles ab 1. Ziffer *)
  112.          Val (NumberStr, Number, NumberEnd);
  113.          IF NumberEnd > 0 THEN        (* ungueltiges Zeichen in NumberStr! *)
  114.             BEGIN                     (* Zahl nicht am Ende von Expr.      *)
  115.                StrPos := StrPos + NumberEnd - 2;
  116.                NumberStr := Copy (NumberStr, 1, Pred(NumberEnd));
  117.                Val (NumberStr, Number, NumberEnd)
  118.             END
  119.          ELSE                     (* hat geklappt, weil am Ende von Expr ! *)
  120.             StrPos := Length(Expr)
  121.       END;
  122.  
  123.       (* ----------------------------------------------------------------- *)
  124.       (*           Symbolbezeichner in der Symbol-Tabelle suchen:          *)
  125.  
  126.       PROCEDURE SearchSymtab;
  127.  
  128.       VAR i    : Calc_Symbols;
  129.           SymOk: BOOLEAN;
  130.  
  131.       BEGIN
  132.          i := Calc_Symbols(0);  SymOk := FALSE;  Symbol := Calc_Err;
  133.          WHILE (i <= Calc_Symbols(Pred(Calc_NoSym))) AND NOT SymOk DO
  134.          BEGIN
  135.             SymOk := (Ident = Calc_Ids[i]);  i := Succ(i)
  136.          END;
  137.          IF SymOk THEN Symbol := Calc_Symbols(Pred(i))  (* Symbol gefunden *)
  138.       END;
  139.  
  140.       (* ----------------------------------------------------------------- *)
  141.  
  142.    BEGIN  (* GetSymbol *)
  143.       LastPos := StrPos;  LastSymbol := Symbol;  Ident := '';
  144.       WHILE (ch = ' ') AND NOT EndOfExpr DO GetChar;
  145.       CASE ch[1] OF
  146.          'A'..'Z': REPEAT      (* Bezeichner f. Operator, Funktion o. Var. *)
  147.                       Ident := Concat (Ident, ch);  GetChar
  148.                    UNTIL NOT (ch[1] IN ['A'..'Z', '0'..'9']);
  149.          '0'..'9': BEGIN  GetNumber;  Ident := 'CONST';  GetChar  END
  150.          ELSE      BEGIN  Ident := ch;  GetChar  END       (* + - * / usw. *)
  151.       END;
  152.       SearchSymtab;
  153.       IF Symbol = Calc_Err THEN      (* Zeichenfolge nicht identifiziert ! *)
  154.          IF Ident[1] IN ['A'..'Z'] THEN    (* nehmen wir sie als Variable! *)
  155.             Symbol := Calc_Var
  156.          ELSE IF Ident <> ' ' THEN Error(LastPos, 'unbekanntes Symbol');
  157.       IF Symbol = Calc_Var THEN
  158.          IF LastSymbol <> Calc_Var THEN
  159.             BEGIN
  160.                Number := SearchVarTab(VarTable, Ident);
  161.                (* entspr. 'CalcDecMod' eine nicht identifizierte Zeichen-
  162.                   folge abweisen oder als neue Variable interpretierten.   *)
  163.                IF CalcDecMod AND (Number = 0.0) THEN
  164.                   Error(LastPos, 'unbekanntes Symbol')
  165.                ELSE IF Number = 0.0 THEN
  166.                BEGIN
  167.                   Number := AddToVarTab(VarTable, Ident);
  168.                   IF Number < 0.0 THEN
  169.                      Error(LastPos, 'Zu viel Variable im Ausdruck')
  170.                END
  171.             END
  172.          ELSE IF NOT EndOfExpr THEN Error(LastPos, 'Operator erwartet')
  173.    END;
  174.  
  175.    (* -------------------------------------------------------------------- *)
  176.    (* Ausdruck kann aus mehreren mit + u. - verknuepften Termen bestehen.  *)
  177.    (* Das ist die Aufgabe von:                                             *)
  178.  
  179.    PROCEDURE Expression;
  180.  
  181.    VAR ExprOp: Calc_Symbols;
  182.  
  183.       (* ----------------------------------------------------------------- *)
  184.       (* Term kann aus mehreren mit den entspr. Operatoren verknuepften    *)
  185.       (* Faktoren bestehen (z.B. Faktor*Faktor). Also:                     *)
  186.  
  187.       PROCEDURE Term;
  188.  
  189.       VAR TermOp: Calc_Symbols;
  190.  
  191.          (* -------------------------------------------------------------- *)
  192.          (* Faktor kann eine Variable, eine Konstante, eine Funktion oder  *)
  193.          (* wieder ein geklammerter Ausdruck sein, die alle noch           *)
  194.          (* potenziert werden koennen! Der Parameter 'fparen' gibt an, ob  *)
  195.          (* es sich bei einer Klammerung um Ausdrucks- oder Funktions-     *)
  196.          (* klammern handelt. Bei letzterem muss vor der Potenzierung noch *)
  197.          (* die Funktion ausgewertet werden! Deshalb:                      *)
  198.  
  199.          PROCEDURE Factor (fparen: BOOLEAN);
  200.  
  201.          VAR FacOp: Calc_Symbols;
  202.  
  203.          BEGIN
  204.             IF Symbol <> Calc_Err THEN
  205.             BEGIN
  206.                CASE Symbol OF
  207.                   Calc_Var: BEGIN
  208.                                Add_To_Queue (Calc_Var, Number);  GetSymbol
  209.                             END;
  210.                 Calc_Const: BEGIN
  211.                                Add_To_Queue (Calc_Const, Number);  GetSymbol
  212.                             END;
  213.                    Calc_Pi: BEGIN
  214.                                Add_To_Queue (Calc_Const, Pi);  GetSymbol
  215.                             END;
  216.                     Calc_E: BEGIN
  217.                                Add_To_Queue (Calc_Const, E);  GetSymbol
  218.                             END;
  219.                    Calc_lp: BEGIN                 (* geklammerter Ausdruck *)
  220.                                GetSymbol;
  221.                                Expression;
  222.                                IF (Symbol <> Calc_rp) THEN
  223.                                   Error (StrPos - Ord(ch[1] > ' '),
  224.                                          Concat(Calc_Ids[Calc_rp],
  225.                                                 ' erwartet'));
  226.                                GetSymbol
  227.                             END;
  228.                   Calc_Pow: ;      (* Potenzieren, wird spaeter erledigt ! *)
  229.                 Calc_Sqr..
  230.                   Calc_Fak: BEGIN                            (* Funktionen *)
  231.                                FacOp := Symbol;
  232.                                GetSymbol;
  233.                                IF (Symbol = Calc_lp) THEN
  234.                                   Factor(TRUE)
  235.                                ELSE
  236.                                   Error (LastPos, Concat(Calc_Ids[Calc_lp],
  237.                                                          ' erwartet'));
  238.                                Add_To_Queue (FacOp, 0)
  239.                             END
  240.                   ELSE              (* Symbol an dieser Stelle unerwartet! *)
  241.                      Error(LastPos, 'hier unerwartet')
  242.                END; (* CASE *)
  243.                IF NOT fparen THEN          (* keine Funktionsklammer, also *)
  244.                   IF Symbol = Calc_Pow THEN               (* potenzieren ! *)
  245.                      IF LastSymbol IN [Calc_Const,Calc_Var,Calc_rp] THEN
  246.                      BEGIN  (* wenn LastSym. ok, Potenzausdruck auswerten: *)
  247.                         GetSymbol;   Factor(FALSE);
  248.                         Add_To_Queue (Calc_Pow, 0);
  249.                      END
  250.                   ELSE                 (* Potenzierung hier nicht moeglich *)
  251.                      Error (Pred(StrPos), 'hier unerwartet');
  252.             END (* IF Symbol <> Calc_Err *)
  253.             ELSE
  254.                Error(StrPos-Ord(EndOfExpr), 'unvollstaendiger Ausdruck');
  255.          END; (* Faktor *)
  256.  
  257.       (* ----------------------------------------------------------------- *)
  258.  
  259.       BEGIN  (* Term *)
  260.          Factor(FALSE);
  261.          IF Symbol IN [Calc_Mul..Calc_kgV] THEN
  262.          BEGIN                      (* mehrere Faktoren im Term enthalten! *)
  263.             TermOp := Symbol;  GetSymbol;
  264.             Term;  Add_To_Queue (TermOp,0)
  265.          END;
  266.       END; (* Term *)
  267.  
  268.    (* -------------------------------------------------------------------- *)
  269.  
  270.    BEGIN  (* Expression *)
  271.       IF Symbol IN [Calc_Add..Calc_Sub] THEN
  272.          BEGIN                              (* Ausdruck beginnt mit + o. - *)
  273.             ExprOp := Symbol;  GetSymbol;  Term;
  274.             IF ExprOp = Calc_Sub THEN             (* 1. Symbol war -, also *)
  275.                Add_To_Queue (Calc_Neg, 0);        (* alles negieren !      *)
  276.          END
  277.       ELSE
  278.          Term;
  279.       WHILE (Symbol IN [Calc_Add..Calc_Sub]) DO
  280.       BEGIN                                   (* es folgen weitere Terme ! *)
  281.          ExprOp := Symbol;  GetSymbol;  Term;
  282.          Add_To_Queue (ExprOp, 0);
  283.       END;
  284.    END; (* Expression *)
  285.  
  286.    (* -------------------------------------------------------------------- *)
  287.  
  288. BEGIN  (* CompileExpression *)
  289.    Symbol := Calc_Err;   ParsError := FALSE;  EndOfExpr := FALSE;
  290.    StrPos := 0;          ch := ' ';           VarTabFlag := (VarTable = NIL);
  291.    IF VarTabFlag THEN VarTable := NewVarTab;
  292.    New (ExprPtr);   ExprPtr^.NextInst := NIL;  ProgPtr := ExprPtr;
  293.    GetSymbol;       Expression;
  294.    IF Symbol <> Calc_EOE THEN Error(LastPos, '";" erwartet');
  295.    CalcResult := NOT ParsError;
  296.    IF ParsError THEN
  297.    BEGIN
  298.       KillExpression(ExprPtr);
  299.       IF VarTabFlag THEN KillVarTab(VarTable)
  300.    END;
  301. END;
  302.  
  303. (* ----------------------------------------------------------------------- *)
  304. (*             Auswertung des in UPN uebersetzten Ausdrucks                *)
  305. (* ----------------------------------------------------------------------- *)
  306.  
  307. FUNCTION CalcExpression (ExprPtr : Calc_Prog;
  308.                          VarTable: Calc_VarTab): Calc_Operand;
  309.  
  310. CONST StackSize = 50;
  311.  
  312. VAR x       : Calc_Operand;
  313.     StackPtr: INTEGER;
  314.     Stack   : ARRAY[1..StackSize] OF Calc_Operand;         (* Rechenstapel *)
  315.  
  316.  
  317.    PROCEDURE Push;                        (* schiebt Zahl auf Rechenstapel *)
  318.  
  319.    BEGIN
  320.       Stack[StackPtr] := x;  StackPtr := Succ(StackPtr)
  321.    END;
  322.  
  323.  
  324.    FUNCTION Pop: Calc_Operand;               (* holt Zahl vom Rechenstapel *)
  325.  
  326.    BEGIN
  327.       StackPtr := Pred(StackPtr);  Pop := Stack[StackPtr]
  328.    END;
  329.  
  330.  
  331. BEGIN  (* CalcExpression *)
  332.    CalcResult := TRUE;
  333.    IF (ExprPtr <> NIL) AND (VarTable <> NIL) THEN
  334.    BEGIN
  335.       ExprPtr := ExprPtr^.NextInst;     (* 1. Eintrag der Queue ist leer ! *)
  336.       StackPtr := 1;  x := 0.0;                (* Laufzeit-Stapel ist leer *)
  337.       WHILE ExprPtr <> NIL DO
  338.       BEGIN
  339.          WITH ExprPtr^ DO
  340.             CASE Instruct OF
  341.                Calc_Const: BEGIN Push;  x := Operand  END;
  342.                Calc_Var  : BEGIN
  343.                               Push;  x := VarTable^[VarIndex].Value
  344.                            END;
  345.             ELSE
  346.                BEGIN
  347.                   CASE Instruct OF
  348.                      Calc_Neg    : x := -x;
  349.                      Calc_Add    : x := Pop+x;
  350.                      Calc_Sub    : x := Pop-x;
  351.                      Calc_Mul    : x := Pop*x;
  352.                      Calc_Dvd    : IF x <> 0.0 THEN x := Pop/x
  353.                                    ELSE CalcError(2, '');
  354.                      Calc_Div    : IF x <> 0.0 THEN
  355.                                      x := Trunc(Pop) DIV Trunc(x)
  356.                                    ELSE CalcError(2, '');
  357.                      Calc_Mod    : IF x <> 0.0 THEN
  358.                                      x := Trunc(Pop) MOD Trunc(x)
  359.                                    ELSE CalcError(2, '');
  360.                      Calc_ggT    : x := ggT(Trunc(Pop),Trunc(x));
  361.                      Calc_kgV    : x := kgV(Trunc(Pop),Trunc(x));
  362.                      Calc_Pow    : x := x_hoch_y(Pop,x);
  363.                      Calc_Sqr    : x := Sqr(x);
  364.                      Calc_Sqrt   : IF x >= 0.0 THEN x := Sqrt(x)
  365.                                    ELSE CalcError(3, 'Sqrt(x): x <= 0');
  366.                      Calc_Exp    : x := Exp(x);
  367.                      Calc_Ln     : IF x > 0.0 THEN x := Ln(x)
  368.                                    ELSE CalcError(3, 'ln(x): x <= 0');
  369.                      Calc_Lg     : x := lg(x);
  370.                      Calc_Ld     : x := ld(x);
  371.                      Calc_Sin    : x := Sin(x);
  372.                      Calc_Cos    : x := Cos(x);
  373.                      Calc_Tan    : x := tan(x);
  374.                      Calc_Cot    : x := cot(x);
  375.                      Calc_ArcSin : x := arcsin(x);
  376.                      Calc_ArcCos : x := arccos(x);
  377.                      Calc_ArcTan : x := ArcTan(x);
  378.                      Calc_ArcCot : x := arccot(x);
  379.                      Calc_Sinh   : x := sinh(x);
  380.                      Calc_Cosh   : x := cosh(x);
  381.                      Calc_Tanh   : x := tanh(x);
  382.                      Calc_Coth   : x := coth(x);
  383.                      Calc_ArcSinh: x := arsinh(x);
  384.                      Calc_ArcCosh: x := arcosh(x);
  385.                      Calc_ArcTanh: x := artanh(x);
  386.                      Calc_ArcCoth: x := arcoth(x);
  387.                      Calc_Abs    : x := abs(x);
  388.                      Calc_Deg    : x := altgrad(x);
  389.                      Calc_Rad    : x := radiant(x);
  390.                      Calc_Rez    : x := kehrwert(x);
  391.                      Calc_Fak    : x := fakultaet(Round(x));
  392.                      ELSE CalcError(0, 'Funktion nicht verfuegbar')
  393.                   END (* CASE *)
  394.                END (* ELSE *)
  395.             END (* CASE *);
  396.          ExprPtr := ExprPtr^.NextInst;
  397.          IF StackPtr > StackSize THEN CalcError(0, 'Stackueberlauf');
  398.          IF NOT CalcResult THEN ExprPtr := NIL
  399.       END; (* WHILE *)
  400.       CalcExpression := x
  401.    END (* THEN *)
  402.    ELSE CalcError(0, 'ungueltiger Ausdruck')
  403.  END;
  404.  
  405. (* ----------------------------------------------------------------------- *)
  406. (*                         Ende von CALC.PAS                               *)
  407.