π Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io π

## to Wordy in the Delphi Pascal Track

Published at Jul 13 2018 · 1 comment
Instructions
Test suite
Solution

Parse and evaluate simple math word problems returning the answer as an integer.

## Iteration 0 β Numbers

Problems with no operations simply evaluate to the number given.

What is 5?

Evaluates to 5.

What is 5 plus 13?

Evaluates to 18.

Handle large numbers and negative numbers.

## Iteration 2 β Subtraction, Multiplication and Division

Now, perform the other three operations.

What is 7 minus 5?

2

What is 6 multiplied by 4?

24

What is 25 divided by 5?

5

## Iteration 3 β Multiple Operations

Handle a set of operations, in sequence.

Since these are verbal word problems, evaluate the expression from left-to-right, ignoring the typical order of operations.

What is 5 plus 13 plus 6?

24

What is 3 plus 2 multiplied by 3?

15 (i.e. not 9)

## Iteration 4 β Errors

The parser should reject:

• Unsupported operations ("What is 52 cubed?")
• Non-math questions ("Who is the President of the United States")
• Word problems with invalid syntax ("What is 1 plus plus 2?")

## Bonus β Exponentials

If you'd like, handle exponentials.

What is 2 raised to the 5th power?

32

## Testing

In order to run the tests for this track, you will need to install DUnitX. Please see the installation instructions for more information.

If Delphi is properly installed, and `*.dpr` file types have been associated with Delphi, then double clicking the supplied `*.dpr` file will start Delphi and load the exercise/project. `control + F9` is the keyboard shortcut to compile the project or pressing `F9` will compile and run the project.

Alternatively you may opt to start Delphi and load your project via. the `File` drop down menu.

### When Questions Come Up

We monitor the Pascal-Delphi support room on gitter.im to help you with any questions that might arise.

### Submitting Exercises

Note that, when trying to submit an exercise, make sure the exercise file you're submitting is in the `exercism/delphi/<exerciseName>` directory.

For example, if you're submitting `ubob.pas` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/delphi/bob/ubob.pas`.

## Source

Inspired by one of the generated questions in the Extreme Startup game. https://github.com/rchatley/extreme_startup

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you may receive assistance from a mentor.

### uWordyTests.pas

``````unit uWordyTests;

interface
uses
DUnitX.TestFramework;

const
CanonicalVersion = '1.5.0';

type

[TestFixture]
WordyTests = class(TObject)
public
//   [Ignore('Comment the "[Ignore]" statement to run the test')]
[TestCase('a number', 'What is 5?,5')]
procedure just_a_number(const aInput: string; const aExpected: integer);

[Ignore]
[TestCase('addition', 'What is 1 plus 1?,2')]
procedure Addition(const aInput: string; const aExpected: integer);

[Ignore]
[TestCase('more addition', 'What is 53 plus 2?,55')]
procedure More_Addition(const aInput: string; const aExpected: integer);

[Ignore]
[TestCase('addition with negative numbers', 'What is -1 plus -10?,-11')]
procedure Addition_with_negative_numbers(const aInput: string; const aExpected: integer);

[Ignore]
[TestCase('large addition', 'What is 123 plus 45678?,45801')]
procedure Large_addition(const aInput: string; const aExpected: integer);

[Ignore]
[TestCase('subtraction', 'What is 4 minus -12?,16')]
procedure Subtraction(const aInput: string; aExpected: integer);

[Ignore]
[TestCase('multiplication', 'What is -3 multiplied by 25?,-75')]
procedure Multiplication(const aInput: string; aExpected: integer);

[Ignore]
[TestCase('division', 'What is 33 divided by -3?,-11')]
procedure Division(const aInput: string; aExpected: integer);

[Ignore]
[TestCase('multiple additions', 'What is 1 plus 1 plus 1?,3')]
procedure Multiple_additions(const aInput: string; aExpected: integer);

[Ignore]
[TestCase('addition and subtraction', 'What is 1 plus 5 minus -2?,8')]
procedure Addition_and_subtraction(const aInput: string; const aExpected: integer);

[Ignore]
[TestCase('multiple subtraction', 'What is 20 minus 4 minus 13?,3')]
procedure Multiple_subtraction(const aInput: string; const aExpected: integer);

[Ignore]
[TestCase('subtraction then addition', 'What is 17 minus 6 plus 3?,14')]
procedure Subtraction_then_addition(const aInput: string; const aExpected: integer);

[Ignore]
[TestCase('multiple multiplication', 'What is 2 multiplied by -2 multiplied by 3?,-12')]
procedure Multiple_multiplication(const aInput: string; aExpected: integer);

[Ignore]
[TestCase('addition and multiplication', 'What is -3 plus 7 multiplied by -2?,-8')]
procedure Addition_and_multiplication(const aInput: string; aExpected: integer);

[Ignore]
[TestCase('multiple division', 'What is -12 divided by 2 divided by -3?,2')]
procedure Multiple_division(const aInput: string; aExpected: integer);

[Ignore]
[TestCase('unknown operation', 'What is 52 cubed?,unknown operation')]
procedure Unknown_operation(const aInput: string; aExpected: string);

[Ignore]
[TestCase('Non math question', 'Who is the President of the United States?,unknown operation')]
procedure Non_math_question(const aInput: string; aExpected: string);

[Ignore]
[TestCase('reject problem missing an operand', 'What is 1 plus?,syntax error')]
procedure problem_missing_an_operand(const aInput: string; aExpected: string);

[Ignore]
[TestCase('reject problem with no operands or operators', 'What is?,syntax error')]
procedure problem_with_no_operands_or_operators(const aInput: string; aExpected: string);

[Ignore]
[TestCase('reject two operations in a row', 'What is 1 plus plus 2?,syntax error')]
procedure reject_two_operations_in_a_row(const aInput: string; aExpected: string);

[Ignore]
[TestCase('reject two numbers in a row', 'What is 1 plus 2 1?,syntax error')]
procedure reject_two_numbers_in_a_row(const aInput: string; aExpected: string);

[Ignore]
[TestCase('reject postfix notation', 'What is 1 2 plus?,syntax error')]
procedure reject_postfix_notation(const aInput: string; aExpected: string);

[Ignore]
[TestCase('reject prefix notation', 'What is plus 1 2?,syntax error')]
procedure reject_prefix_notation(const aInput: string; aExpected: string);
end;

implementation
uses uWordy;

procedure WordyTests.Addition(const aInput: string; const aExpected: integer);
begin
end;

aExpected: integer);
begin
end;

const aExpected: integer);
begin
end;

const aExpected: integer);
begin
end;

procedure WordyTests.Division(const aInput: string; aExpected: integer);
begin
end;

procedure WordyTests.problem_missing_an_operand(const aInput: string;
aExpected: string);
var MyProc: TTestLocalMethod;
begin
MyProc := procedure
begin
end;

Assert.WillRaiseWithMessage(MyProc, EInvalidProblem, aExpected);
end;

procedure WordyTests.just_a_number(const aInput: string;
const aExpected: integer);
begin
end;

const aExpected: integer);
begin
end;

const aExpected: integer);
begin
end;

aExpected: integer);
begin
end;

procedure WordyTests.Multiple_division(const aInput: string;
aExpected: integer);
begin
end;

procedure WordyTests.Multiple_multiplication(const aInput: string;
aExpected: integer);
begin
end;

procedure WordyTests.Multiple_subtraction(const aInput: string;
const aExpected: integer);
begin
end;

procedure WordyTests.Multiplication(const aInput: string; aExpected: integer);
begin
end;

procedure WordyTests.Subtraction(const aInput: string; aExpected: integer);
begin
end;

const aExpected: integer);
begin
end;

procedure WordyTests.Unknown_operation(const aInput: string; aExpected: string);
var MyProc: TTestLocalMethod;
begin
MyProc := procedure
begin
end;

Assert.WillRaiseWithMessage(MyProc, EInvalidProblem, aExpected);
end;

procedure WordyTests.problem_with_no_operands_or_operators(const aInput: string; aExpected: string);
var MyProc: TTestLocalMethod;
begin
MyProc := procedure
begin
end;

Assert.WillRaiseWithMessage(MyProc, EInvalidProblem, aExpected);
end;

procedure WordyTests.Non_math_question(const aInput: string; aExpected: string);
var MyProc: TTestLocalMethod;
begin
MyProc := procedure
begin
end;

Assert.WillRaiseWithMessage(MyProc, EInvalidProblem, aExpected);
end;

procedure WordyTests.reject_postfix_notation(const aInput: string;
aExpected: string);
var MyProc: TTestLocalMethod;
begin
MyProc := procedure
begin
end;

Assert.WillRaiseWithMessage(MyProc, EInvalidProblem, aExpected);
end;

procedure WordyTests.reject_prefix_notation(const aInput: string;
aExpected: string);
var MyProc: TTestLocalMethod;
begin
MyProc := procedure
begin
end;

Assert.WillRaiseWithMessage(MyProc, EInvalidProblem, aExpected);
end;

procedure WordyTests.reject_two_numbers_in_a_row(const aInput: string;
aExpected: string);
var MyProc: TTestLocalMethod;
begin
MyProc := procedure
begin
end;

Assert.WillRaiseWithMessage(MyProc, EInvalidProblem, aExpected);
end;

procedure WordyTests.reject_two_operations_in_a_row(const aInput: string;
aExpected: string);
var MyProc: TTestLocalMethod;
begin
MyProc := procedure
begin
end;

Assert.WillRaiseWithMessage(MyProc, EInvalidProblem, aExpected);
end;

initialization
TDUnitX.RegisterTestFixture(WordyTests);
end.``````
``````unit uWordy;

interface
uses
System.Sysutils, System.Generics.Collections;

type
TArithmeticOp = TFunc<integer, integer, integer>;
TOperatorDict = TDictionary<string, TArithmeticOp>;

TWordy = class
private
class var
FNbOperands: integer;
FNbOperators: integer;
FOperators: TOperatorDict;
FWords: TArray<string>;
class procedure BuildOperators;
class function IsValid(aInput: string): boolean;
public

end;

EInvalidProblem = class(Exception);

implementation

class procedure TWordy.BuildOperators;
begin
FOperators := TOperatorDict.create;
function(op1, op2: integer): integer
begin
result := op1 + op2
end);
function(op1, op2: integer): integer
begin
result := op1 - op2
end);
function(op1, op2: integer): integer
begin
result := op1 * op2
end);
function(op1, op2: integer): integer
begin
result := op1 div op2
end);
end;

class function TWordy.IsValid(aInput: string): boolean;
var
i: integer;
begin
aInput := StringReplace(aInput, ' by ', ' ', [rfReplaceAll]);
FWords := copy(aInput, 1, length(aInput) - 1).Split([' ']);
FNbOperands := 0;
FNbOperators := 0;
BuildOperators;
for i := 2 to High(FWords) do
begin
if (StrToIntDef(FWords[i], 0) <> 0) then
inc(FNbOperands)
else if (FOperators.ContainsKey(FWords[i])) then
inc(FNbOperators);
end;
result := not ((FNbOperands < 2) or (FNbOperators < 1) or (FNbOperands - FNbOperators <> 1));
end;

var
zFunc, zFunc2: TArithmeticOp;
begin
if not IsValid(aInput) then
raise EInvalidProblem.Create('Invalid Problem');

zFunc := FOperators[FWords[3]];
if (FNbOperators = 1) then
result := zFunc(StrToInt(FWords[2]), StrToInt(FWords[4]))
else
begin
zFunc2 := FOperators[FWords[5]];
result := zFunc2(zFunc(StrToInt(FWords[2]), StrToInt(FWords[4])),
StrToInt(FWords[6]));
end;
end;

end.``````