Arithmetic evaluation/Delphi
<lang Delphi> program Arithmetic_evaluation;
{$APPTYPE CONSOLE}
System.SysUtils;
type
NodeType = (binop, number, error);
pAstNode = ^tAstNode;
tAstNode = record typ: NodeType; operation: char; first, second: pAstNode; value: integer; end;
TASTParse = class private fCh: char; FExpression: string; FResult: Integer; fAst: pAstNode; FEvalError: boolean; procedure Get; procedure skipWhitespace; function parseAddSub: pAstNode; function parseValue: pAstNode; function parseMulDiv: pAstNode; procedure disposeTree(tree: pAstNode); function newBinOp(op: char; left: pAstNode): pAstNode; function eval(ast: pAstNode): integer; public function TryParse(Expression: string; var aResult: Integer): boolean; overload; function TryParse(Expression: string): boolean; overload; function Parse(Expression: string): Integer; overload; function Parse(Expression: string; var aResult: Integer): boolean; overload; constructor Create(); overload; constructor Create(Expression: string); overload; destructor Destroy; override; property Result: Integer read FResult; end;
function TASTParse.newBinOp(op: char; left: pAstNode): pAstNode; var
node: pAstNode;
begin
new(node); node^.typ := binop; node^.operation := op; node^.first := left; node^.second := nil; newBinOp := node;
end;
function TASTParse.Parse(Expression: string): Integer; begin
TryParse(Expression); Result := FResult;
end;
function TASTParse.Parse(Expression: string; var aResult: Integer): boolean; begin
Result := TryParse(Expression); aResult := FResult;
end;
procedure TASTParse.disposeTree(tree: pAstNode); begin
if tree^.typ = binop then begin if (tree^.first <> nil) then disposeTree(tree^.first); if (tree^.second <> nil) then disposeTree(tree^.second) end; dispose(tree);
end;
procedure TASTParse.skipWhitespace; var
ch: char;
function isWhite: boolean; begin isWhite := false; if not (fCh = #0) then if fCh = ' ' then isWhite := true end;
begin
while isWhite do Get;
end;
function TASTParse.TryParse(Expression: string; var aResult: Integer): boolean; var
value: integer;
begin
FExpression := Expression; Get; fAst := parseAddSub; Result := not ((fast^.typ = error) or not (fCh = #0)); if not Result then exit;
FEvalError := False; value := eval(fAst); Result := not FEvalError;
if not Result then exit;
FResult := value; aResult := FResult;
end;
function TASTParse.TryParse(Expression: string): boolean; begin
Result := TryParse(Expression, FResult);
end;
function TASTParse.parseAddSub: pAstNode; var
node1, node2: pAstNode; continue: boolean;
begin
node1 := parseMulDiv; if node1^.typ <> error then begin continue := true; while continue and not (fCh = #0) do begin skipWhitespace; if fCh in ['+', '-'] then begin node1 := newBinop(fCh, node1); get; node2 := parseMulDiv; if (node2^.typ = error) then begin disposeTree(node1); node1 := node2; continue := false end else node1^.second := node2 end else continue := false end; end; parseAddSub := node1;
end;
function TASTParse.parseMulDiv: pAstNode; var
node1, node2: pAstNode; continue: boolean;
begin
node1 := parseValue; if node1^.typ <> error then begin continue := true; while continue and not (fch = #0) do begin skipWhitespace; if fCh in ['*', '/'] then begin node1 := newBinop(fCh, node1); get; node2 := parseValue; if (node2^.typ = error) then begin disposeTree(node1); node1 := node2; continue := false end else node1^.second := node2 end else continue := false end; end; parseMulDiv := node1;
end;
function TASTParse.parseValue: pAstNode; var
node: pAstNode; value: integer; neg: boolean;
begin
node := nil; skipWhitespace; if fCh = '(' then begin get; node := parseAddSub; if node^.typ <> error then begin skipWhitespace; if fCh = ')' then get else begin disposeTree(node); new(node); node^.typ := error; end end end else if fCh in ['0'..'9', '+', '-'] then begin neg := fCh = '-'; if fCh in ['+', '-'] then get; value := 0; if fCh in ['0'..'9'] then begin while fCh in ['0'..'9'] do begin value := 10 * value + (ord(fCh) - ord('0')); get end; new(node); node^.typ := number; if (neg) then node^.value := -value else node^.value := value end end; if node = nil then begin new(node); node^.typ := error; end; parseValue := node
end;
function TASTParse.eval(ast: pAstNode): Integer; begin
with ast^ do case typ of number: Result := value; binop: case operation of '+': Result := eval(first) + eval(second); '-': Result := eval(first) - eval(second); '*': Result := eval(first) * eval(second); '/': Result := eval(first) div eval(second); end; error: begin Result := 0; FEvalError := True; end; end
end;
constructor TASTParse.Create(Expression: string); begin
Create; Parse(Expression);
end;
constructor TASTParse.Create; begin
fCh := #0; FExpression := ; FResult := 0; fAst := nil;
end;
destructor TASTParse.Destroy; begin
if Assigned(fAst) then disposeTree(fAst); inherited;
end;
procedure TASTParse.Get; begin
if FExpression.isempty then begin fCh := #0; exit; end; fCh := FExpression.Substring(0, 1)[1]; delete(FExpression, 1, 1);
end;
const
EXP = '1 + 2*(3 - 2*(3 - 2)*((2 - 4)*5 - 22/(7 + 2*(3 - 1)) - 1)) + 1';
begin
with TASTParse.Create do begin writeln(EXP, ' = ', parse(EXP)); Free; end; Readln;
end.</lang>
- Output:
1 + 2*(3 - 2*(3 - 2)*((2 - 4)*5 - 22/(7 + 2*(3 - 1)) - 1)) + 1 = 60