Oxygene for Java version of Arithmetic

Arithmetic using Reverse Polish Notation (RPN)

Introduction

You will see that much of the code remains in its original form. We draw your attention to the following points regarding the conversion from pure Pascal to Oxygene for Java.

  • Arrays are changed to be zero-based to be accepted (even though the value with index zero might not be used).
  • Individual characters in a string are zero-based.
  • System.out.Printf is used for formatting real numbers.
  • The assignment Result := ... instead of stackPop := ... must be used for the return value of function stackPop.
  • "Float" is used instead of "Real" in Oxygene for Java.

At the command prompt we typed (1) cd C:\Oxygen to change directory to the program folder, (2) msbuild arithmetic.oxygene to compile to byte-code in arithmetic.jar then (3) java -jar bin\Release\arithmetic.jar to execute the program. You can open the project file in Visual Studio and execute the program with a click on the green triangle.

The program (arithmetic.pas)

namespace arithmetic;
{
    Copyright (c) 2011 Peter Hearnshaw

    Licensed under the Apache License, Version 2.0 (the "License"); you may
    not use this file except in compliance with the License, as described at
    http://www.apache.org/licenses/ and http://www.pp4s.co.uk/licenses/

    Converted to Oxygene for Java by PPS.
}
interface

var
  inputStr : string;
  inputChars : array[0 .. 255] of SByte;
  inputStrWithSpaces : string;
  ExpressionArray : array[0 .. 80] of string;
  ExpressionArrayIsOperator, //parallel to ExpressionArray, stating whether num or op
	  VariablisedExpressionArray,
		RPNVariablisedExpressionArray: array[0 .. 80] of SByte;
  ExpressionArrayLength : Integer;
  VariableStorageArray : array[0 .. 34] of real;
  i, RPNcount : Integer;
  isOperator : boolean := false;
  currentChar : char;
  tempString : string;
  numOfVariables : integer := 0;
  Popped, Scanned, CurrentChrByte : SByte;
  finalAnswer, tempReal1, tempReal2 : real;
  myRealStack : array[0 .. 100] of real;
  indexOfTopOfStack : integer := -1;

implementation

method stackPush(value : real);
begin
  inc(indexOfTopOfStack);
  myRealStack[indexOfTopOfStack] := value;
end;

method stackPop : real;
begin
  Result := myRealStack[indexOfTopOfStack]; //Use "Result" not name of method
  dec(indexOfTopOfStack);
end;

begin
  for i := 0 to 80 do
    begin
      ExpressionArray[i] := '';
    end;
  writeln('Please input an expression (with no spaces): ');
  System.in.read(inputChars);
  inputStrWithSpaces := new String(inputChars);
  inputStr := inputStrWithSpaces.trim;
  //inputStr := '(1+2.5*(2.762+53.1*(2+-5*-4*(2+3)))+-61.7)';
  writeln('Expression: ' + inputStr);

  //PROCESS INITIAL EXPRESSION
  expressionArrayLength := 0;
  currentChar := inputStr[0];
  if (ord(currentChar) > 47) then
    expressionArrayLength := 1;    //expressionArrayLength becomes one if
    //first char is a number because numbers will be added to the end of the
    //current array position
  for i := 0 to length(inputStr) - 1 do
    begin
      currentChar := inputStr[i];
      //if current char is - and last char was an operator other than )
      if (ord(currentChar) = 45) and (isOperator = true) and (ord(inputStr[i - 1]) <> 41) then
        begin
          isOperator := false;
          inc(expressionArrayLength);
          ExpressionArrayIsOperator[expressionArrayLength] := 0;
        end
      else
        begin
          if (ord(currentChar) < 48) and (ord(currentChar) <> 46) then
            begin
              isOperator := true;
              inc(expressionArrayLength);
              ExpressionArrayIsOperator[expressionArrayLength] := 1;
            end;
          if (ord(currentChar) > 47) and (isOperator = true) then
            begin
              isOperator := false;
              inc(expressionArrayLength);
              ExpressionArrayIsOperator[expressionArrayLength] := 0;
            end;
        end;
      ExpressionArray[expressionArrayLength] := ExpressionArray[expressionArrayLength]
                                       +  currentChar;
    end;

  //VARIABLISE EXPRESSION
  //by variablising I mean turning 41 + 21 into a + b, 'a' stored in array as
  //41 and 'b' as 21
  numOfVariables := 0;
  for i := 1 to expressionArrayLength do
    begin
      if (ExpressionArrayIsOperator[i] = 0) then //i.e. is a number
        begin
          inc(numOfVariables);
          tempString := ExpressionArray[i];
          VariableStorageArray[numOfVariables] := new Float(tempString);
          VariablisedExpressionArray[i] := 96 + numOfVariables;  //first
                                          //variable will be 'a' then 'b'...
        end;
      if (ExpressionArrayIsOperator[i] = 1) then //i.e. is an operator
        begin
          tempString := ExpressionArray[i];
          VariablisedExpressionArray[i] := ord(tempString[0]);
        end;
    end;

  //PRINT OUT
  System.out.print('Expression In Variables: ');
  for i := 1 to expressionArrayLength do
    begin
      System.out.print(chr(VariablisedExpressionArray[i]));
      if (i <> expressionArrayLength) then
        System.out.print(',');
    end;
  writeln;
  writeln('Variables:');
  for i := 1 to numOfVariables do
    begin
      System.out.print('     '+ chr(96 + i) +' = ');
      System.out.printf('%.3f', VariableStorageArray[i]);
      writeln;
    end;
  finalAnswer := 0;

  //CONVERT TO REVERSE POLISH NOTATION
  indexOfTopOfStack := 0;
  RPNcount := 0;
  for i := 1 to expressionArrayLength do
    begin
      if VariablisedExpressionArray[i] > 47 then  //is a number
        begin
          inc(RPNcount);
          RPNVariablisedExpressionArray[RPNcount] :=
                                           VariablisedExpressionArray[i];
        end;
      if VariablisedExpressionArray[i] < 48 then  //is an operator
        begin
          //Don't bother at all with this if there isn't anything on the stack
          if (indexOfTopOfStack <> 0) and (VariablisedExpressionArray[i] <> 40)
                                      and (VariablisedExpressionArray[i] <> 41) then
            begin
              repeat
                popped := Math.Round(stackPop);
                stackPush(popped);
                if popped = 40 then
                  begin
                    scanned := 100;  //This makes sure the program will escape
                  //the repeat, to immediately push the operator
                  end
                else
                  begin
                    //poppedItem := popped;
                    if (popped = 42) or (popped = 47) then  //   * or /
                      begin
                        popped := 2;  //priority 2
                      end
                    else
                      begin
                        popped := 1;  //priority 1
                      end;
                    scanned := VariablisedExpressionArray[i];
                    if (scanned = 42) or (scanned = 47) then
                      begin
                        scanned := 2; //priority 2
                      end
                    else
                      begin
                        scanned := 1; //priority 1
                      end;
                    if (scanned <= popped) then
                      // Pop the stack to the end of the RPN array
                      begin
                        inc(RPNcount);
                        popped := Math.Round(stackPop);
                        RPNVariablisedExpressionArray[RPNcount] := popped;
                      end;
                  end;
              until (scanned > popped) or (indexOfTopOfStack = 0);
            end;
          stackPush(VariablisedExpressionArray[i]);  //VERY important
          if (VariablisedExpressionArray[i] = 41) then
            begin
              stackPop; //just to get off the ) we just added
              popped := Math.Round(stackPop);
              expressionArrayLength := expressionArrayLength - 2;
              {This takes two away from the current expression array length
              because two brackets have been removed from the array. Pop the
              operators within the brackets to the end of the RPN array.}
              while popped <> 40 do
                begin
                  inc(RPNcount);
                  RPNVariablisedExpressionArray[RPNcount] := popped;
                  popped := Math.Round(stackPop);
                end;
            end;
        end;
    end;
   //Pop any remaining operators on the stack to the RPN array
  if indexOfTopOfStack > 0 then
    begin
      for i := 0 to indexOfTopOfStack - 1 do
        begin
          inc(RPNcount);
          popped := Math.Round(stackPop);
          RPNVariablisedExpressionArray[RPNcount] := popped;
        end;
    end;
  System.out.print('In Reverse Polish Notation: ');
  for i := 1 to expressionArrayLength do
    begin
      System.out.print(chr(RPNVariablisedExpressionArray[i]));
    end;
  writeln;

 {Evaluate the RPN.  Before the operators are used, the
  stack will contain numbers pushed with the line of code
  in the else clause below:
  stackPush(VariableStorageArray[currentChrByte - 96]);}

  indexOfTopOfStack := 0;
  for i:= 1 to expressionArrayLength do
    begin
      currentChrByte := RPNVariablisedExpressionArray[i];
      case currentChrByte of
        43 : begin
               tempReal1 := stackPop;
               tempReal2 := stackPop;
               stackPush(tempReal1 + tempReal2);
             end;
        42 : begin
               tempReal1 := stackPop;
               tempReal2 := stackPop;
               tempReal2 := tempReal2 * tempReal1;
               stackPush(tempReal2);
             end;
        45 : begin
               tempReal1 := stackPop;
               tempReal2 := stackPop;
               stackPush(tempReal2 - tempReal1);
             end;
        47 : begin
               tempReal1 := stackPop;
               tempReal2 := stackPop;
               tempReal2 := tempReal2 / tempReal1;
               stackPush(tempReal2);
             end;
      else
        begin
          //Push the number represented by its lower case letter to the stack
          stackPush(VariableStorageArray[currentChrByte - 96]);
        end;
    end;
  end;
  finalAnswer := stackPop;

  System.out.printf('Final Answer: %.3f', finalAnswer );
  System.in.read;
end.

The project file (arithmetic.oxygene)

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
  <PropertyGroup>
    <ProductVersion>3.5</ProductVersion>
    <OutputType>Exe</OutputType>
    <Configuration Condition="'$(Configuration)' == ''">Release</Configuration>
    <Name>Arithmetic</Name>
    <RootNamespace>org.me.arithmetic</RootNamespace>    
    <AssemblyName>arithmetic</AssemblyName>
    <StartupClass />
    <DefaultUses />
  </PropertyGroup>  
  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
    <Optimize>true</Optimize>
    <OutputPath>.\bin\Release</OutputPath>
    <GenerateDebugInfo>False</GenerateDebugInfo>
    <GenerateMDB>False</GenerateMDB>
    <EnableAsserts>False</EnableAsserts>
    <TreatWarningsAsErrors>False</TreatWarningsAsErrors>
    <CaptureConsoleOutput>False</CaptureConsoleOutput>
    <StartMode>Project</StartMode>
    <RegisterForComInterop>False</RegisterForComInterop>
    <CpuType>anycpu</CpuType>
    <RuntimeVersion>v25</RuntimeVersion>
    <XmlDoc>False</XmlDoc>
    <XmlDocWarningLevel>WarningOnPublicMembers</XmlDocWarningLevel>
    <EnableUnmanagedDebugging>False</EnableUnmanagedDebugging>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="rt.jar" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="Properties\" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="arithmetic.pas">
      <SubType>Code</SubType>
    </Compile>
  </ItemGroup>
  <Import Project="$(MSBuildExtensionsPath)\RemObjects Software\Oxygene\RemObjects.Oxygene.Cooper.targets" />
</Project>
Programming - a skill for life!

Arithmetic using Reverse Polish Notation (RPN)