home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / spezial / 04 / quellen / calc.pas next >
Encoding:
Pascal/Delphi Source File  |  1979-12-31  |  18.8 KB  |  436 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; Write('*** Fehler in CompileExpression:');
  48.          ClrEol; WriteLn;
  49.          Write (' ',Expr);ClrEol; Writeln;
  50.          Write (' ':ErrPos, '^'); ClrEol; Writeln;
  51.          Write (ErrMsg, '!'); Clreol; Writeln; ClrEol;
  52.          WriteLn; ClrEol
  53.       END;
  54.       ParsError := TRUE;  Symbol := Calc_Err
  55.    END;
  56.  
  57.    (* -------------------------------------------------------------------- *)
  58.    (*           naechsten Befehl an die UPN-Queue anhaengen:               *)
  59.    (* Dabei wird mittels 'HeapAvail' der noch freie Heap geprueft. Wegen   *)
  60.    (* des eventuellen Aufrufs der Error-Prozedur wird nicht nur Platz fuer *)
  61.    (* ein Queue-Element benoetigt - deshalb ein paar Bytes mehr, um eine   *)
  62.    (* Heap-Stack-Kollision zu vermeiden !                                  *)
  63.  
  64.    PROCEDURE Add_To_Queue (op: Calc_Symbols; x: Calc_Operand);
  65.  
  66.    VAR UPN_Entry: Calc_Prog;
  67.  
  68.    BEGIN
  69.       IF HeapAvail > 160.0 THEN     (* hier: noch 160 Bytes im Heap frei ? *)
  70.          BEGIN
  71.             New (UPN_Entry);
  72.             WITH UPN_Entry^ DO
  73.             BEGIN
  74.                NextInst := NIL;
  75.                Instruct := op;
  76.                CASE op OF
  77.                   Calc_Var  : VarIndex := Trunc(x);
  78.                   Calc_Const: Operand  := x
  79.                END
  80.             END;
  81.             ProgPtr^.NextInst := UPN_Entry;
  82.             ProgPtr           := ProgPtr^.NextInst
  83.          END
  84.       ELSE Error(1, 'nicht genuegend freier Speicher fuer den Ausdruck')
  85.    END;
  86.  
  87.    (* -------------------------------------------------------------------- *)
  88.    (*           naechstes Symbol aus der Zeichenkette holen:               *)
  89.  
  90.    PROCEDURE GetSymbol;
  91.  
  92.       (* ----------------------------------------------------------------- *)
  93.       (*           holt naechstes Zeichen aus der Zeichenkette:            *)
  94.  
  95.       PROCEDURE GetChar;
  96.  
  97.       BEGIN
  98.          ch := ' ';  StrPos := Succ(StrPos);
  99.          EndOfExpr := (StrPos > Length(Expr));
  100.          IF NOT EndOfExpr THEN ch := UpCase (Expr[StrPos])
  101.       END;
  102.  
  103.       (* ----------------------------------------------------------------- *)
  104.       (*        holt und wandelt Ziffernfolge aus der Zeichenkette:        *)
  105.       (* die Val-Prozedur von Turbo Pascal moechte in der Zeichenkette nur *)
  106.       (* gueltige Zeichen einer Real-Zahl haben, NumberEnd zeigt auf das   *)
  107.       (* erste ungueltige Zeichen! Deshalb folgendes:                      *)
  108.  
  109.       PROCEDURE GetNumber;
  110.  
  111.       VAR NumberStr: Calc_String;
  112.           NumberEnd,posi: INTEGER;
  113.  
  114.       BEGIN
  115.          NumberStr := Copy (Expr, StrPos, 255); (* nimm alles ab 1. Ziffer *)
  116.          NumberStr := NumberStr + '     ';
  117.          posi := 1;
  118.          while (not (numberstr[posi] in ['e','E']))
  119.            and (posi < length(numberstr))
  120.          do posi := succ(posi);
  121.          if numberstr[posi] in ['e','E'] THEN
  122.          BEGIN
  123.            if numberstr[posi+1] in ['+','-'] THEN posi := succ(posi);
  124.            if not (numberstr[posi+1] in ['0'..'9'])
  125.              THEN error(StrPos+posi,'unvollstaendiger Ausdruck')
  126.              else if
  127.               ((numberstr[posi+1] = '3') and (numberstr[posi+2] in ['7'..'9']))
  128.            or ((numberstr[posi+1] > '3') and (numberstr[posi+2] in ['0'..'9']))
  129.                 THEN error(StrPos+posi,'ausserhalb des Real-Bereichs');
  130.          END;
  131.          Val (NumberStr, Number, NumberEnd);
  132.          IF NumberEnd > 0 THEN        (* ungueltiges Zeichen in NumberStr! *)
  133.             BEGIN                     (* Zahl nicht am Ende von Expr.      *)
  134.                StrPos := StrPos + NumberEnd - 2;
  135.                NumberStr := Copy (NumberStr, 1, Pred(NumberEnd));
  136.                Val (NumberStr, Number, NumberEnd)
  137.             END
  138.          ELSE                     (* hat geklappt, weil am Ende von Expr ! *)
  139.             StrPos := Length(Expr)
  140.       END;
  141.  
  142.       (* ----------------------------------------------------------------- *)
  143.       (*           Symbolbezeichner in der Symbol-Tabelle suchen:          *)
  144.  
  145. PROCEDURE searchsymtab;
  146.  
  147. VAR symok: BOOLEAN;
  148.  
  149. BEGIN
  150.   symok := FALSE;
  151.   symbol := calc_err;
  152.   WHILE (symbol < calc_end) AND NOT symok DO
  153.     BEGIN
  154.       symbol := Succ(symbol);
  155.       symok := (ident = calc_ids[symbol]);
  156.     END;
  157.   IF NOT symok
  158.     THEN
  159.       symbol := calc_err
  160. END;
  161.  
  162.  
  163.       (* ----------------------------------------------------------------- *)
  164.  
  165.    BEGIN  (* GetSymbol *)
  166.       LastPos := StrPos;  LastSymbol := Symbol;  Ident := '';
  167.       WHILE (ch = ' ') AND NOT EndOfExpr DO GetChar;
  168.       CASE ch[1] OF
  169.          'A'..'Z': REPEAT      (* Bezeichner f. Operator, Funktion o. Var. *)
  170.                       Ident := Concat (Ident, ch);  GetChar
  171.                    UNTIL NOT (ch[1] IN ['A'..'Z', '0'..'9']);
  172.          '0'..'9','.': BEGIN  GetNumber;  Ident := 'CONST';  GetChar  END
  173.          ELSE      BEGIN  Ident := ch;  GetChar  END       (* + - * / usw. *)
  174.       END;
  175.       SearchSymtab;
  176.       IF Symbol = Calc_Err THEN      (* Zeichenfolge nicht identifiziert ! *)
  177.          IF Ident[1] IN ['A'..'Z'] THEN    (* nehmen wir sie als Variable! *)
  178.             Symbol := Calc_Var
  179.          ELSE IF Ident <> ' ' THEN Error(LastPos, 'unbekanntes Symbol');
  180.       IF Symbol = Calc_Var THEN
  181.          IF LastSymbol <> Calc_Var THEN
  182.             BEGIN
  183.                Number := SearchVarTab(VarTable, Ident);
  184.                (* entspr. 'CalcDecMod' eine nicht identifizierte Zeichen-
  185.                   folge abweisen oder als neue Variable interpretierten.   *)
  186.                IF CalcDecMod AND (Number = 0.0) THEN
  187.                   Error(LastPos, 'unbekanntes Symbol')
  188.                ELSE IF Number = 0.0 THEN
  189.                BEGIN
  190.                   Number := AddToVarTab(VarTable, Ident);
  191.                   IF Number < 0.0 THEN
  192.                      Error(LastPos, 'Zu viel Variable im Ausdruck')
  193.                END
  194.             END
  195.          ELSE IF NOT EndOfExpr THEN Error(LastPos, 'Operator erwartet')
  196.    END;
  197.  
  198.    (* -------------------------------------------------------------------- *)
  199.    (* Ausdruck kann aus mehreren mit + u. - verknuepften Termen bestehen.  *)
  200.    (* Das ist die Aufgabe von:                                             *)
  201.  
  202. {$A-}     (* rekursiven Code unter CP/M erzeugen ! *)
  203.  
  204.    PROCEDURE Expression;
  205.  
  206.    VAR ExprOp: Calc_Symbols;
  207.  
  208.       (* ----------------------------------------------------------------- *)
  209.       (* Term kann aus mehreren mit den entspr. Operatoren verknuepften    *)
  210.       (* Faktoren bestehen (z.B. Faktor*Faktor). Also:                     *)
  211.  
  212.       PROCEDURE Term;
  213.  
  214.       VAR TermOp: Calc_Symbols;
  215.  
  216.          (* -------------------------------------------------------------- *)
  217.          (* Faktor kann eine Variable, eine Konstante, eine Funktion oder  *)
  218.          (* wieder ein geklammerter Ausdruck sein, die alle noch           *)
  219.          (* potenziert werden koennen! Der Parameter 'fparen' gibt an, ob  *)
  220.          (* es sich bei einer Klammerung um Ausdrucks- oder Funktions-     *)
  221.          (* klammern handelt. Bei letzterem muss vor der Potenzierung noch *)
  222.          (* die Funktion ausgewertet werden! Deshalb:                      *)
  223.  
  224.          PROCEDURE Factor (fparen: BOOLEAN);
  225.  
  226.          VAR FacOp: Calc_Symbols;
  227.  
  228.          BEGIN
  229.             IF Symbol <> Calc_Err THEN
  230.             BEGIN
  231.                CASE Symbol OF
  232.                   Calc_Var: BEGIN
  233.                                Add_To_Queue (Calc_Var, Number);  GetSymbol
  234.                             END;
  235.                 Calc_Const: BEGIN
  236.                                Add_To_Queue (Calc_Const, Number);  GetSymbol
  237.                             END;
  238.                    Calc_Pi: BEGIN
  239.                                Add_To_Queue (Calc_Const, Pi);  GetSymbol
  240.                             END;
  241.                     Calc_E: BEGIN
  242.                                Add_To_Queue (Calc_Const, E);  GetSymbol
  243.                             END;
  244.                    Calc_lp: BEGIN                 (* geklammerter Ausdruck *)
  245.                                GetSymbol;
  246.                                Expression;
  247.                                IF (Symbol <> Calc_rp) THEN
  248.                                   Error (StrPos - Ord(ch[1] > ' '),
  249.                                          Concat(Calc_Ids[Calc_rp],
  250.                                                 ' erwartet'));
  251.                                GetSymbol
  252.                             END;
  253.                   Calc_Pow: ;      (* Potenzieren, wird spaeter erledigt ! *)
  254.                 Calc_Sqr..
  255.                   Calc_Fak: BEGIN                            (* Funktionen *)
  256.                                FacOp := Symbol;
  257.                                GetSymbol;
  258.                                IF (Symbol = Calc_lp) THEN
  259.                                   Factor(TRUE)
  260.                                ELSE
  261.                                   Error (LastPos, Concat(Calc_Ids[Calc_lp],
  262.                                                          ' erwartet'));
  263.                                Add_To_Queue (FacOp, 0)
  264.                             END
  265.                   ELSE              (* Symbol an dieser Stelle unerwartet! *)
  266.                      Error(LastPos, 'hier unerwartet')
  267.                END; (* CASE *)
  268.                IF NOT fparen THEN          (* keine Funktionsklammer, also *)
  269.                   IF Symbol = Calc_Pow THEN               (* potenzieren ! *)
  270.                      IF LastSymbol IN [Calc_Const,Calc_Var,Calc_rp,
  271.                                        Calc_PI,Calc_e] THEN
  272.                      BEGIN  (* wenn LastSym. ok, Potenzausdruck auswerten: *)
  273.                         GetSymbol;   Factor(FALSE);
  274.                         Add_To_Queue (Calc_Pow, 0);
  275.                      END
  276.                   ELSE                 (* Potenzierung hier nicht moeglich *)
  277.                      Error (Pred(StrPos), 'hier unerwartet');
  278.             END (* IF Symbol <> Calc_Err *)
  279.             ELSE
  280.                Error(StrPos-Ord(EndOfExpr), 'unvollstaendiger Ausdruck');
  281.          END; (* Faktor *)
  282.  
  283.       (* ----------------------------------------------------------------- *)
  284.  
  285.       BEGIN  (* Term *)
  286.          Factor(FALSE);
  287.          IF Symbol IN [Calc_Mul..Calc_kgV] THEN
  288.          BEGIN                      (* mehrere Faktoren im Term enthalten! *)
  289.             TermOp := Symbol;  GetSymbol;
  290.             Term;  Add_To_Queue (TermOp,0)
  291.          END;
  292.       END; (* Term *)
  293.  
  294.    (* -------------------------------------------------------------------- *)
  295.  
  296.    BEGIN  (* Expression *)
  297.       IF Symbol IN [Calc_Add..Calc_Sub] THEN
  298.          BEGIN                              (* Ausdruck beginnt mit + o. - *)
  299.             ExprOp := Symbol;  GetSymbol;  Term;
  300.             IF ExprOp = Calc_Sub THEN             (* 1. Symbol war -, also *)
  301.                Add_To_Queue (Calc_Neg, 0);        (* alles negieren !      *)
  302.          END
  303.       ELSE
  304.          Term;
  305.       WHILE (Symbol IN [Calc_Add..Calc_Sub]) DO
  306.       BEGIN                                   (* es folgen weitere Terme ! *)
  307.          ExprOp := Symbol;  GetSymbol;  Term;
  308.          Add_To_Queue (ExprOp, 0);
  309.       END;
  310.    END; (* Expression *)
  311. {$a+}   (* ab hier wieder absoluten Code unter CP/M erzeugen *)
  312.  
  313.    (* -------------------------------------------------------------------- *)
  314.  
  315. BEGIN  (* CompileExpression *)
  316.    Symbol := Calc_Err;   ParsError := FALSE;  EndOfExpr := FALSE;
  317.    StrPos := 0;          ch := ' ';           VarTabFlag := (VarTable = NIL);
  318.    IF VarTabFlag THEN VarTable := NewVarTab;
  319.    New (ExprPtr);   ExprPtr^.NextInst := NIL;  ProgPtr := ExprPtr;
  320.    GetSymbol;       Expression;
  321.    IF Symbol <> Calc_EOE THEN Error(LastPos, '";" erwartet');
  322.    CalcResult := NOT ParsError;
  323.    IF ParsError THEN
  324.    BEGIN
  325.       KillExpression(ExprPtr);
  326.       IF VarTabFlag THEN KillVarTab(VarTable)
  327.    END;
  328. END;
  329.  
  330. (* ----------------------------------------------------------------------- *)
  331. (*             Auswertung des in UPN uebersetzten Ausdrucks                *)
  332. (* ----------------------------------------------------------------------- *)
  333.  
  334. FUNCTION CalcExpression (ExprPtr : Calc_Prog;
  335.                          VarTable: Calc_VarTab): Calc_Operand;
  336.  
  337. CONST StackSize = 50;
  338.  
  339. VAR x       : Calc_Operand;
  340.     StackPtr: INTEGER;
  341.     Stack   : ARRAY[1..StackSize] OF Calc_Operand;         (* Rechenstapel *)
  342.  
  343.  
  344.    PROCEDURE Push;                        (* schiebt Zahl auf Rechenstapel *)
  345.  
  346.    BEGIN
  347.       Stack[StackPtr] := x;  StackPtr := Succ(StackPtr)
  348.    END;
  349.  
  350.  
  351.    FUNCTION Pop: Calc_Operand;               (* holt Zahl vom Rechenstapel *)
  352.  
  353.    BEGIN
  354.       StackPtr := Pred(StackPtr);  Pop := Stack[StackPtr]
  355.    END;
  356.  
  357.  
  358. BEGIN  (* CalcExpression *)
  359.    CalcResult := TRUE;
  360.    IF (ExprPtr <> NIL) AND (VarTable <> NIL) THEN
  361.    BEGIN
  362.       ExprPtr := ExprPtr^.NextInst;     (* 1. Eintrag der Queue ist leer ! *)
  363.       StackPtr := 1;  x := 0.0;                (* Laufzeit-Stapel ist leer *)
  364.       WHILE ExprPtr <> NIL DO
  365.       BEGIN
  366.          WITH ExprPtr^ DO
  367.             CASE Instruct OF
  368.                Calc_Const: BEGIN Push;  x := Operand  END;
  369.                Calc_Var  : BEGIN
  370.                               Push;  x := VarTable^[VarIndex].Value
  371.                            END;
  372.             ELSE
  373.                BEGIN
  374.                   CASE Instruct OF
  375.                      Calc_Neg    : x := -x;
  376.                      Calc_Add    : x := Pop+x;
  377.                      Calc_Sub    : x := Pop-x;
  378.                      Calc_Mul    : x := Pop*x;
  379.                      Calc_Dvd    : IF x <> 0.0 THEN x := Pop/x
  380.                                    ELSE CalcError(2, '');
  381.                      Calc_Div    : IF Trunc(x) <> 0 THEN
  382.                                      x := Trunc(Pop) DIV Trunc(x)
  383.                                    ELSE CalcError(2, '');
  384.                      Calc_Mod    : IF Trunc(x) <> 0 THEN
  385.                                      x := Trunc(Pop) MOD Trunc(x)
  386.                                    ELSE CalcError(2, '');
  387.                      Calc_ggT    : x := ggT(Trunc(Pop),Trunc(x));
  388.                      Calc_kgV    : x := kgV(Trunc(Pop),Trunc(x));
  389.                      Calc_Pow    : x := x_hoch_y(Pop,x);
  390.                      Calc_Sqr    : x := Sqr(x);
  391.                      Calc_Sqrt   : IF x >= 0.0 THEN x := Sqrt(x)
  392.                                    ELSE CalcError(3, 'Sqrt(x): x <= 0');
  393.                      Calc_Exp    : x := Exp(x);
  394.                      Calc_Ln     : IF x > 0.0 THEN x := Ln(x)
  395.                                    ELSE CalcError(3, 'ln(x): x <= 0');
  396.                      Calc_Lg     : x := lg(x);
  397.                      Calc_Ld     : x := ld(x);
  398.                      Calc_Sin    : x := Sin(x);
  399.                      Calc_Cos    : x := Cos(x);
  400.                      Calc_Tan    : x := tan(x);
  401.                      Calc_Cot    : x := cot(x);
  402.                      Calc_ArcSin : x := arcsin(x);
  403.                      Calc_ArcCos : x := arccos(x);
  404.                      Calc_ArcTan : x := ArcTan(x);
  405.                      Calc_ArcCot : x := arccot(x);
  406.                      Calc_Sinh   : x := sinh(x);
  407.                      Calc_Cosh   : x := cosh(x);
  408.                      Calc_Tanh   : x := tanh(x);
  409.                      Calc_Coth   : x := coth(x);
  410.                      Calc_ArcSinh: x := arsinh(x);
  411.                      Calc_ArcCosh: x := arcosh(x);
  412.                      Calc_ArcTanh: x := artanh(x);
  413.                      Calc_ArcCoth: x := arcoth(x);
  414.                      Calc_Abs    : x := abs(x);
  415.                      Calc_Deg    : x := altgrad(x);
  416.                      Calc_Rad    : x := radiant(x);
  417.                      Calc_Rez    : x := kehrwert(x);
  418.                      Calc_Fak    : x := fakultaet(Round(x));
  419.                      Calc_Int    : x := Int(x);
  420.                      Calc_Sig    : x := Sign(x);
  421.                      ELSE CalcError(0, 'Funktion nicht verfuegbar')
  422.                   END (* CASE *)
  423.                END (* ELSE *)
  424.             END (* CASE *);
  425.          ExprPtr := ExprPtr^.NextInst;
  426.          IF StackPtr > StackSize THEN CalcError(0, 'Stackueberlauf');
  427.          IF NOT CalcResult THEN ExprPtr := NIL
  428.       END; (* WHILE *)
  429.       CalcExpression := x
  430.    END (* THEN *)
  431.    ELSE CalcError(0, 'ungueltiger Ausdruck')
  432.  END;
  433.  
  434. (* ----------------------------------------------------------------------- *)
  435. (*                         Ende von CALC.PAS                               *)
  436.