Portia Virtual Machine Language
Version: |
2004.0 |
Date: |
April 4, 2004 |
Author: |
Kevin Albrecht |
Website: |
1.1 File Formats and Extensions
3.11 Miscellaneous Instructions
3.12 High-Level Language Error Reporting Support
Portia is a language and virtual machine implemented as an abstract stack machine. It is named after the heroine in William Shakespeare’s The Merchant of Venice.
There are two types of Portia files: assembler and bytecode. Assembler is an ASCII format and uses the extension PRA. Bytecode is the binary format and uses the extension PRB.
The virtual machine runs four stacks concurrently.
Stack |
Purpose |
call-stack |
recursive routine calling and returning |
local-stack |
holds local values during routine invocation |
param-stack |
holds routine parameters and routine return values |
exp-stack |
used by instructions to evaluate expressions |
This document uses a notation for stack access borrowed from Forth, a stack-based programming language.
( b a -- a b )
^ ^
top of stack before |
|
top of stack after
The left side of the double dashes shows the top of the stack prior to the instruction execution and after it. If a variable has a type written next to it, attached by a colon, the variable must be of the type shown. For example:
( x:int y:int -- z:bool )
This would mean that the instruction accepts two integers from the top of the stack and produces one boolean.
All programs must end with a stop instruction.
# Hello World in Portia
push "Hello world!"
cout
push 10 #
asc # output a newline character
cout #
stop
Directives are not actually instructions. They give information to the virtual machine language processor.
The preprocessor accepts two commands: file inclusion and comments.
$file.por
Directly include the file “file.por” into the current file.
#this a comment
Everything on a line following a “#” will be ignored.
:somewhere
Defines a label at the point in the program called “somewhere”.
Instructions are executed directly by the virtual machine.
This instruction has no effect.
nop
These pseudo-instructions declare one or more variables and their type.
db var1 var2 ...
Declare one or more bool variables.
di var1 var2 ...
Declare one or more int variables.
dr var1 var2 ...
Declare one or more rat variables.
ds var1 var2 ...
Declare one or more str variables.
These instructions control the sequence of instructions executed.
jmp x:label
Transfer control to the instruction after the label.
jt x:label
exp-stack: ( y:bool -- )
Transfer control to the instruction after the label if the value on the stack is true.
jf x:label
exp-stack: ( y:bool -- )
Transfer control to the instruction after the label if x is false.
call x:label
call-stack: ( -- y:label )
Push the current location onto the call-stack, then transfer control to x.
ret
call-stack: ( y:label -- )
Pop a location off of the call-stack and transfer control to it.
stop
Terminate execution of the program.
These instructions convert one type to another.
tob
exp-stack: ( x:any -- y:bool )
Convert value in x to bool and put in y.
toi
exp-stack: ( x:any -- y:int )
Convert value in x to int and put in y.
tor
exp-stack: ( x:any -- y:rat )
Convert value in x to rat and put in y.
tos
exp-stack: ( x:any -- y:str )
Convert value in x to str and put in y.
copy
exp-stack: ( x -- x x )
Duplicate the item on the top of the stack.
swap
exp-stack: ( x y -- y x )
Swap the top two items on the stack.
pop y:t
popl y:t
popp y:t
( x:t -- )
Pop the top value off the stack and place it in the variable y. The three versions of pop operate on the exp-stack, local-stack, and param-stack, respectively.
push x
pushl x
pushp x
( -- x )
Push the value of expression x onto the stack. The three versions of push operate on the exp-stack, local-stack, and param-stack, respectively.
addi
addr
exp-stack: ( x:type1 y:type2 -- z:type3 )
Add x and y, and put value in z. The two versions of add require type3 to be int or rat, respectively. The types of x and y can be int or rat.
subi
subr
exp-stack: ( x:type1 y:type2 -- z:type3 )
Subtract x and y, and put value in z. The two versions of sub require type3 to be int or rat, respectively. The types of x and y can be int or rat.
muli
mulr
exp-stack: ( x:type1 y:type2 -- z:type3 )
Multiply x and y, and put value in z. The two versions of mul require type3 to be int or rat, respectively. The types of x and y can be int or rat.
divi
divr
exp-stack: ( x:type1 y:type2 -- z:type3 )
Divide x and y, and put value in z. The two versions of div require type3 to be int or rat, respectively. The types of x and y can be int or rat.
powi
powr
exp-stack: ( x:type1 y:type2 -- z:type3 )
Raise x to the power of y, and put value in z. The two versions of pow require type3 to be int or rat, respectively. The types of x and y can be int or rat.
modi
modr
exp-stack: ( x:int y:int -- z:type )
Divide x by y, and put remainder in z. The two versions of mod require type to be int or rat, respectively.
neg
exp-stack: ( x:t -- y:t )
Negate x and put the result in y. The type t of x and y are the same and must be a number type (int or rat).
eq
exp-stack: ( x:type1 y:type2 -- z:bool )
Check if x and y are equal, and put boolean result in z. The types of x and y can both be numbers, both be bool, or both be str.
neq
exp-stack: ( x:type1 y:type2 -- z:bool )
Check if x and y are not equal, and put boolean result in z. The types of x and y can both be numbers, both be bool, or both be str.
lt
exp-stack: ( x:type1 y:type2 -- z:bool )
Check if x is less than y, and put boolean result in z. The types of x and y must both be numbers.
lteq
exp-stack: ( x:type1 y:type2 -- z:bool )
Check if x is less than or equal to y, and put boolean result in z. The types of x and y must both be numbers.
gt
exp-stack: ( x:type1 y:type2 -- z:bool )
Check if x is greater than y, and put boolean result in z. The types of x and y must both be numbers.
gteq
exp-stack: ( x:type1 y:type2 -- z:bool )
Check if x is greater than or equal to y, and put boolean result in z. The types of x and y must both be numbers.
or
exp-stack: ( x:bool y:bool -- z:bool )
Perform logical or on x and y, and put boolean result in z.
and
exp-stack: ( x:bool y:bool -- z:bool )
Perform logical and on x and y, and put boolean result in z.
not
exp-stack: ( x:bool -- y:bool )
Perform logical not on x, and put result in y.
cat
exp-stack: ( x:str y:str -- z:str )
Append y to the end of x and put resulting string in z.
cout
exp-stack: ( x -- )
Print x on the console.
cin
exp-stack: ( -- x:str )
Get a string from the user and put it in x.
The following file input/output instructions read to and write from text files.
fio_or filename:str
exp-stack: ( -- handle:int )
Open a file for reading called filename and put a file handle integer in handle.
fio_ow filename:str
exp-stack: ( -- handle:int )
Open a file for writing called filename and put a file handle integer in handle.
fio_c handle:int
exp-stack: ( -- )
Close the file designated by handle.
fio_eof handle:int
exp-stack: ( -- reached:bool )
Check if the end-of-file has been reached in the file designated by handle. If it has, put “true” in reached, otherwise, put “false” in reached.
fio_r handle:int
exp-stack: ( -- input:str )
Read a line of text from the file designated by handle into input.
fio_w handle:int
exp-stack: ( output:str -- )
Write text from output to the file designated by handle.
asc
exp-stack: ( x:int -- y:str )
Get the character corresponding to the ASCII code in x and put it in y.
rand
exp-stack: ( -- x:int )
Generate a random integer and put it in x. The value is in the range 0 to at least 32768, inclusive. Best used in conjunction with mod.
The following instructions are generated by the compiler for high-level languages (HLLs), allowing the Portia virtual machine to report the location of runtime errors in the original HLL source file.
dbgon
Activates HLL error reporting. This should only appear once in the input file and at the very beginning.
dbgfile string_literal
Sets the error reporting file to the file specified by the string literal.
dbgline int_literal
Sets the error reporting line number to the number specified by the integer literal.