⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unitcexpression.pas

📁 Delphi的另一款钢琴软件
💻 PAS
字号:
unit unitCExpression;

interface

uses Classes, SysUtils;

type
TValueType = (vInteger, vString, vReal);
TValue = record
  tp : TValueType;
  iVal : Integer;
  sVal : string;
  rVal : extended
end;


TStrValue = class
private
  fValue : string;
public
  constructor Create (const AValue : string);
  property Value : string read fValue;
end;

procedure CalcExpression (const st : string; defines : TStringList; var value : TValue);

implementation

type
  TOperator = (opNop, opEq, opNotEq, opLessEq, opGtrEq, opLess, opGtr,
               opAdd, opSub, opLor, opOr, opMult, opDiv, opIDiv, opMod, oplAnd, opAnd, opNot);

procedure DoOp (op : TOperator; var x : TValue; y : TValue);

  procedure AssignBool (bool, rev : boolean);
  begin
    x.tp := vInteger;
    if rev then
      bool := not bool;
    if bool then
      x.iVal := -1
    else
      x.iVal := 0
  end;

  procedure TypeMismatch;
  begin
    raise Exception.Create('Type mismatch in expression');
  end;

begin
  if (x.tp = vReal) and (y.tp = vInteger) then
  begin
    y.rVal := y.iVal;
    y.tp := vReal
  end;

  if (x.tp = vInteger) and (y.tp = vReal) then
  begin
    x.rVal := x.iVal;
    x.tp := vReal
  end;

  if x.tp <> y.tp then
    TypeMismatch;

  if op in [opAnd, opOr, opLand, opLor, opIDiv, opMod] then
    if x.tp <> vInteger then
      TypeMismatch;

  if op in [opSub, opMult, opDiv] then
    if (x.tp <> vInteger) and (x.tp <> vReal) then
      TypeMismatch;

  case op of
    opEq,
    opNotEq : case x.tp of
                vInteger : AssignBool (x.iVal = y.iVal, op = opNotEq);
                vString  : AssignBool (x.sVal = y.sVal, op = opNotEq);
                vReal    : AssignBool (x.rVal = y.rVal, op = opNotEq);
              end;

    opLess,
    opGtrEq : case x.tp of
                vInteger : AssignBool (x.iVal < y.iVal, op = opGtrEq);
                vString  : AssignBool (x.sVal < y.sVal, op = opGtrEq);
                vReal    : AssignBool (x.rVal < y.rVal, op = opGtrEq);
              end;

    opGtr,
    opLessEq : case x.tp of
                vInteger : AssignBool (x.iVal > y.iVal, op = opLessEq);
                vString  : AssignBool (x.sVal > y.sVal, op = opLessEq);
                vReal    : AssignBool (x.rVal > y.rVal, op = opLessEq);
              end;

    oplAnd : AssignBool ((x.iVal and y.iVal) <> 0, false);
    oplOr  : AssignBool ((x.iVal or y.iVal) <> 0, false);
    opAnd  : x.iVal := x.iVal and y.iVal;
    opOr   : x.iVal := x.iVal or y.iVal;

    opAdd  : case x.tp of
               vInteger : x.iVal := x.iVal + y.iVal;
               vReal    : x.rVal := x.rVal + y.rVal;
               vString  : x.sVal := x.sVal + y.sVal
             end;

    opSub  : if x.tp = vInteger then
               x.iVal := x.iVal - y.iVal
             else
               x.rVal := x.rVal - y.rVal;

    opMult : if x.tp = vInteger then
               x.iVal := x.iVal * y.iVal
             else
               x.rVal := x.rVal * y.rVal;

    opDiv  : if x.tp = vInteger then
             begin
               x.rVal := x.iVal div y.iVal;
               x.tp := vReal
             end
             else
               x.rVal := x.rVal / y.rVal;

    opIDiv : x.iVal := x.iVal div y.iVal;
    opMod  : x.iVal := x.iVal mod y.iVal;
  end;
end;

procedure DoUnaryOp (op : TOperator; var x : TValue);
begin
  if x.tp = vString then
    raise Exception.Create ('Type mismatch in expression');

  case op of
    opSub : if x.tp = vInteger then
              x.iVal := -x.iVal
            else
              x.rVal := -x.rVal;
    opNot : if x.tp <> vInteger then
              raise Exception.Create ('Type mismatch in expression')
            else
              if x.iVal <> 0 then
                x.iVal := 0
              else
                x.iVal := -1

  end
end;

procedure CalcExpression (const st : string; defines : TStringList; var value : TValue);
var
  pos : Integer;
  ch : char;

  function CalcBoolean : TValue; forward;

  function GetChar : char;
  begin
    if pos < Length (st) then
    begin
      ch := st [pos + 1];
      Inc (pos)
    end
    else
      ch := #0;
    result := ch;
  end;

  function GetNonWhitespace : char;
  begin
    repeat GetChar until not (ch in [' ', #9]);
    result := ch
  end;

  procedure SkipWhitespace;
  begin
    if ch in [' ', #9] then
      GetNonWhitespace
  end;

  function CalcId : TValue;
  var
    id : string;
    idx : Integer;
  begin
    id := ch;
    while GetChar in ['A'..'Z', 'a'..'z', '0'..'9', '_'] do
      id := id + ch;
    SkipWhitespace;

    if id = 'defined' then
    begin
      if (ch = '(') then
      begin
        GetNonWhitespace;
        id := ch;
        while GetChar in ['A'..'Z', 'a'..'z', '0'..'9', '_'] do
          id := id + ch;
        SkipWhitespace;
        if ch <> ')' then
          raise Exception.Create ('Missing '')''');
        idx := defines.IndexOf(id);
        result.tp := vInteger;
        if idx = -1 then
          result.iVal := 0
        else
          result.iVal := -1
      end
      else
        raise Exception.Create('''('' expected');
      GetNonWhitespace;
      exit;
    end;

    idx := defines.IndexOf(id);
    if idx >= 0 then
      CalcExpression (TStrValue (defines.Objects [idx]).fValue, defines, result)
    else
    begin
      result.tp := vInteger;
      result.iVal := 0
    end
  end;

  function CalcNumber : TValue;
  var
    n : string;
    hexFlag : boolean;
    dotPos : Integer;
  begin
    n := ch;
    hexFlag := False;
    dotPos := 0;

    if GetChar in ['x', 'X'] then
    begin
      n := '';
      hexFlag := True;
      GetChar;
    end;

    while (ch in ['0'..'9']) or (hexFlag and (ch in ['A'..'F', 'a'..'f'])) do
    begin
      n := n + ch;

      if GetChar = '.' then
        if (dotPos = 0) and not hexFlag then
        begin
          dotPos := Length (n);
          n := n + ch;
          GetChar
        end
        else
          break
    end;
    SkipWhitespace;

    if dotPos = Length (n) then
    begin
      Delete (n, Length (n), 1);
      ch := '.';
      Dec (pos);
      dotPos := 0;
    end;

    if hexFlag then
    begin
      result.tp := vInteger;
      result.iVal := StrToInt ('$' + n)
    end
    else
      if dotPos = 0 then
      begin
        result.tp := vInteger;
        result.iVal := StrToInt (n)
      end
      else
      begin
        result.tp := vReal;
        result.rVal := StrToFloat (n)
      end
  end;

  function CalcCString : TValue;
  var
    st : string;
  begin
    st := '';
    while GetChar <> #0 do
    case ch of
      '"' : break;
      '\' :
        case GetChar of
          '"' : st := st + '"';
          'n' : st := st + #10;
          'r' : st := st + #13;
          't' : st := st + #9;
          '\' : st := st + '\';
          '0' : st := st + #0;
          else
            raise EParserError.Create('Invalid escape sequence');
        end;
      else
        st := st + ch
    end;
    GetChar;
    result.tp := vString;
    result.sVal := st
  end;

  function CalcTerm : TValue;
  begin
    case ch of
      '(' : begin
              GetNonWhitespace;
              result := CalcBoolean;
              if ch <> ')' then
              raise Exception.Create ('Mismatched parentheses')
            end;
       'A'..'Z', 'a'..'z', '_' :
              result := CalcId;
       '0'..'9' :
              result := CalcNumber;
       '"' :
              result := CalcCString;
    end
  end;

  function CalcSignedTerm : TValue;
  begin
    if ch = '+' then
    begin
      GetNonWhitespace;
      result := CalcSignedTerm
    end
    else
      if ch = '-' then
      begin
        GetNonWhitespace;
        result := CalcSignedTerm;
        DoUnaryOp (opSub, result);
      end
      else
        if ch = '!' then
        begin
          GetNonWhitespace;
          result := CalcSignedTerm;
          DoUnaryOp (opNot, result)
        end
        else
          result := CalcTerm
  end;

  function CalcMultiplication : TValue;
  var
    op : TOperator;
  begin
    result := CalcSignedTerm;
    while ch in ['*', '/', '\', '%', '&'] do
    begin

      op := opNop;
      case ch of
       '*' : begin op := opMult; GetChar; end;
       '/' : begin op := opDiv;  GetChar; end;
       '\' : begin op := opIDiv; GetChar; end;
       '%' : begin op := opMod;  GetChar; end;
       '&' : if GetChar = '&' then
             begin
               op := oplAnd;
               GetChar
             end
             else
               op := opAnd;
      end;
      SkipWhitespace;
      if op <> opNop then
        DoOp (op, result, CalcSignedTerm)
      else
        break
    end
  end;

  function CalcAddition : TValue;
  var
    op : TOperator;
  begin
    result := CalcMultiplication;
    while ch in ['+', '-', '|'] do
    begin
      op := opNop;
      case ch of
        '+' : begin op := opAdd; GetChar; end;
        '-' : begin op := opSub; GetChar; end;
        '|' : if GetChar = '|' then
              begin
                GetChar;
                op := oplor
              end
              else
                op := opOr;
      end;
      SkipWhitespace;
      if op <> opNop then
        DoOp (op, result, CalcMultiplication)
      else
        break
    end
  end;

  function CalcBoolean : TValue;
  var
    op : TOperator;
  begin
    result := CalcAddition;

    while ch in ['=', '|', '<', '>'] do
    begin

      op := opNop;
      case ch of
        '=' : if GetChar = '=' then
              begin
                GetChar;
                op := opEq
              end;
        '!' : if GetChar = '=' then
              begin
                GetChar;
                op := opNotEq
              end;
        '<' : if GetChar = '=' then
              begin
                op := opLessEq;
                GetChar
              end
              else
                op := opLess;

        '>' : if GetChar = '=' then
              begin
                op := opGtrEq;
                GetChar;
              end
              else
                op := opGtr
      end;

      SkipWhitespace;

      if op <> opNop then
        DoOp (op, result, CalcAddition)
      else
        break
    end
  end;

begin
  pos := 0;

  GetNonWhitespace;
  value := CalcAddition;
end;

{ TStrValue }

constructor TStrValue.Create(const AValue: string);
begin
  fValue := AValue;
end;

end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -