Difference between revisions of "An arbitrary-precision calculator"

From PROSE Programming Language - Wiki
Jump to: navigation, search
(Add Step 1)
(Add Step 2)
Line 79: Line 79:
  
 
== Step 2 - Accepting basic commands ==
 
== Step 2 - Accepting basic commands ==
 +
 +
The <code>help</code> and <code>quit</code> commands should be easy to implement.  We need a function which we'll call <code>getcmd()</code> that reads the next command from standard input and strips the trailing newline character.  This demonstrates returning a value from a function, and introduces us to the concept of the encoded attribute value (or <code>XVALUE</code>).  Here is the function code:
 +
 +
<pre>
 +
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +
% getcmd()
 +
%
 +
% Gets next command from stdin (stripping newline character)
 +
%
 +
% Arguments:
 +
% None
 +
%
 +
% Returns:
 +
% String, or empty string if input is exhausted
 +
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 +
.func_getcmd
 +
reg/load P15, ![.prose.sys.io]
 +
 +
% Display command prompt to stderr
 +
attr/mod P15, [psStreamError], [> ]
 +
 +
% Collect input
 +
attr/copy P0, P15, [psStreamIn]
 +
reg/jmpeq &[.getcmd_null], P0, NULL
 +
 +
% Strip trailing newline character
 +
reg/load A, (P0)
 +
op/decr
 +
error/jmp &[.no_strip], ![.prose.error.sys.OutOfRange]
 +
reg/load P2, (P0, A)
 +
op/shr P2, P2, #24
 +
reg/jmpneq &[.no_strip], P2, #10
 +
reg/save P0, (A)
 +
func/rtn P0
 +
 +
.no_strip
 +
error/clr
 +
func/rtn P0
 +
 +
.getcmd_null
 +
func/rtn []
 +
</pre>
 +
 +
The only difference in the above code from previous tutorials is that a string is being passed to the <code>func/rtn</code> instruction.  This necessitates an additional argument to the corresponding <code>func/def</code> instruction which we insert into the <code>._init</code> section.  We'll re-write that section now to pre-load some attribute definitions into registers <code>P0</code> and <code>P1</code>:
 +
 +
<pre>
 +
._init
 +
attr/load P0, [psString], P1, [psIndex]
 +
 +
%
 +
% Function definitions
 +
%
 +
func/def [main],    &[.main]
 +
func/def [title],    &[.func_title]
 +
func/def [getcmd],  &[.func_getcmd], P0
 +
local/rtn
 +
</pre>
 +
 +
Here the <code>psString</code> attribute is being passed to the 3rd argument of <code>func/def</code>.  This identifies the type of data that the function will return.  If omitted, or <code>NULL</code>, then the function cannot return a value.  Function return values can only be a variable type, i.e. a type that can be assigned to a program variable.  We discuss variable types later on in this tutorial.
 +
 +
When the <code>getcmd()</code> function is called, it will return the next line from standard input.  All functions that return values will return them as '''encoded attribute values'''.  This is data that is encoded for a specific attribute type, in this case <code>psString</code>.  It will be saved into a register as the type <code>PSUNIT_TYPE_XVALUE</code>.  When the <code>func/rtn</code> instruction is used with a value, that value can be provided in the correct encoding (in which case it is simply returned as-is), or if not it will be re-encoded automatically by the PROSE engine before the return completes.  This re-encoding will usually take place via a byte string representation, because all variable types can be represented in byte string format.
  
 
== Resources from this tutorial ==
 
== Resources from this tutorial ==

Revision as of 22:07, 29 December 2010

PAL Tutorial - An arbitrary-precision calculator

In release 0.7.x, only the PROSE Assembly Language (PAL) is available, and then only a subset of those instructions. So be aware, it's very low-level programming at this time. To learn more about the PROSE Programming Language, visit http://prose.sourceforge.net.

It is suggested you begin with the following articles before attempting this tutorial.

A full list of available tutorials for the PROSE Assembly Language can be found on the tutorials index page.

This tutorial works through an example PAL program that implements a simple arbitrary-precision calculator.

Objectives

The objective is to write a PAL program that will allow for simple two-operand calculations on integers, floating-point numbers and rational numbers. This will demonstrate the use of variables and functions as well as basic integration with the GNU MP (Multi-Precision) library. Note that this is based on GMP 4.3.2.

The program will understand the following commands supplied on the standard input:

    M+N             adds two numbers M and N and displays the result
    M*N             multiplies two numbers M and N and displays the result
    M-N             subtracts two numbers M and N and displays the result
    M/N             divides two numbers M and N and displays the result
    M~N             divides two numbers M and N and displays the result
                        (when the number type is rational)
    int             set number type to integer
    flt             set number type to floating-point
    rat             set number type to rational
    help            displays this help page
    debug           toggles debug mode ON and OFF
    quit            exits this program

Integers, floating-point numbers and rational numbers will all take the form accepted by the GNU MP library. Floating-point numbers at this time have a default precision set to at least 64 bits. Rational numbers are represented by X/Y where / separates the numerator from the denominator. Because of this, the ~ symbol will be used by the program for requesting a division operation with rational numbers.

Step 1 - Displaying a title

The first step is to display a title when the program is launched. This should be straightforward if you have followed previous tutorials:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Simple arbitrary-precision calculator
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
._init
func/def	[main],     &[.main]
func/def	[title],    &[.func_title]
local/rtn

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% main()
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.main
func/call	NULL, [title]
func/rtn

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% title()
%
% Display title of program to stderr
%
% Arguments:
%	None
%
% Returns:
%	None
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.func_title
attr/mod	![.prose.sys.io], [psStreamError], [
pscalc: a simple arbitrary-precision calculator
Type 'help' for a list of commands or 'quit' to exit
]
func/rtn

Step 2 - Accepting basic commands

The help and quit commands should be easy to implement. We need a function which we'll call getcmd() that reads the next command from standard input and strips the trailing newline character. This demonstrates returning a value from a function, and introduces us to the concept of the encoded attribute value (or XVALUE). Here is the function code:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% getcmd()
%
% Gets next command from stdin (stripping newline character)
%
% Arguments:
%	None
%
% Returns:
%	String, or empty string if input is exhausted
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.func_getcmd
reg/load	P15, ![.prose.sys.io]

% Display command prompt to stderr
attr/mod	P15, [psStreamError], [> ]

% Collect input
attr/copy	P0, P15, [psStreamIn]
reg/jmpeq	&[.getcmd_null], P0, NULL

% Strip trailing newline character
reg/load	A, (P0)
op/decr
error/jmp	&[.no_strip], ![.prose.error.sys.OutOfRange]
reg/load	P2, (P0, A)
op/shr		P2, P2, #24
reg/jmpneq	&[.no_strip], P2, #10
reg/save	P0, (A)
func/rtn	P0

.no_strip
error/clr
func/rtn	P0

.getcmd_null
func/rtn	[]

The only difference in the above code from previous tutorials is that a string is being passed to the func/rtn instruction. This necessitates an additional argument to the corresponding func/def instruction which we insert into the ._init section. We'll re-write that section now to pre-load some attribute definitions into registers P0 and P1:

._init
attr/load	P0, [psString], P1, [psIndex]

%
% Function definitions
%
func/def	[main],     &[.main]
func/def	[title],    &[.func_title]
func/def	[getcmd],   &[.func_getcmd],	P0
local/rtn

Here the psString attribute is being passed to the 3rd argument of func/def. This identifies the type of data that the function will return. If omitted, or NULL, then the function cannot return a value. Function return values can only be a variable type, i.e. a type that can be assigned to a program variable. We discuss variable types later on in this tutorial.

When the getcmd() function is called, it will return the next line from standard input. All functions that return values will return them as encoded attribute values. This is data that is encoded for a specific attribute type, in this case psString. It will be saved into a register as the type PSUNIT_TYPE_XVALUE. When the func/rtn instruction is used with a value, that value can be provided in the correct encoding (in which case it is simply returned as-is), or if not it will be re-encoded automatically by the PROSE engine before the return completes. This re-encoding will usually take place via a byte string representation, because all variable types can be represented in byte string format.

Resources from this tutorial

Further reading

See the other tutorials available for the PROSE Assembly Language on the tutorials index page.

PROSE is released with detailed manual pages that describe how PAL operates, and how each instruction is used. These manual pages can be read using the man command, for example man pal_intro or man pal_commands, or from the project links on the main page of this wiki.