Skip to content
forked from tsoding/porth

It's like Forth but in Python

License

Notifications You must be signed in to change notification settings

AqilaRifti/porth

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Porth

WARNING! This language is a work in progress!

It's like Forth but written in Python. But I don't actually know since I never programmed in Forth, I only heard that it's some sort of stack-based programming language. Porth is also stack-based programming language. Which makes it just like Forth am I rite?

Porth is planned to be (these are not the selling points, but rather milestones of the development)

  • Compiled
  • Native
  • Stack-based (just like Forth)
  • Turing-complete
  • Self-hosted (Python is used only as an initial bootstrap, once the language is mature enough we gonna rewrite it in itself)
  • Statically typed (the type checking is probably gonna be similar to the WASM validation)

Example

Hello, World:

"Hello, World\n" 1 1 syscall3

Simple program that prints numbers from 0 to 99 in an ascending order:

100 0 while 2dup > do
    dup print 1 +
end

Quick Start

Simulation

Simulation simply interprets the program.

$ cat program.porth
34 35 + print
$ ./porth.py sim program.porth
69

Compilation

Compilation generates assembly code, compiles it with nasm, and then links it with GNU ld. So make sure you have both available in your $PATH.

$ cat program.porth
34 35 + print
$ ./porth.py com program.porth
[INFO] Generating ./program.asm
[CMD] nasm -felf64 ./program.asm
[CMD] ld -o ./program ./program.o
$ ./program
69

Testing

Test cases are located in ./tests/ folder. The *.txt files are the expected outputs of the corresponding programs.

Run ./test.py script to execute the programs and assert their outputs:

$ ./test.py

To updated expected output files run the record subcommand:

$ ./test.py record

The ./examples/ contains programs that are ment for showcasing the language rather then testing it, but we still can them for testing just like the stuff in ./tests/:

$ ./test.py -f ./examples/
$ ./test.py -f ./examples/ record

Language Reference

This is what the language supports so far. Since the language is a work in progress the exact set of operations is the subject to change.

Stack Manipulation

  • <integer> - push the integer onto the stack. Right now the integer is anything that is parsable by int function.
push(<integer>)
  • <string> - push size and address of the string literal onto the stack. A string literal is a sequence of character enclosed with ".
size = len(<string>)
push(n)
ptr = static_memory_alloc(n)
copy(ptr, <string>)
push(ptr)
  • dup - duplicate an element on top of the stack.
a = pop()
push(a)
push(a)
  • 2dup - duplicate pair.
b = pop()
a = pop()
push(a)
push(b)
push(a)
push(b)
  • swap - swap 2 elements on the top of the stack.
a = pop()
b = pop()
push(a)
push(b)
  • drop - drops the top element of the stack.
pop()
  • print - print the element on top of the stack in a free form to stdout and remove it from the stack.
a = pop()
print(a)
  • over
a = pop()
b = pop()
push(b)
push(a)
push(b)

Comparison

  • = - checks if two elements on top of the stack are equal. Removes the elements from the stack and pushes 1 if they are equal and 0 if they are not.
a = pop()
b = pop()
push(int(a == b))
  • != - checks if two elements on top of the stack are not equal.
a = pop()
b = pop()
push(int(a != b))
  • > - checks if the element below the top greater than the top.
b = pop()
a = pop()
push(int(a > b))
  • < - checks if the element below the top less than the top.
b = pop()
a = pop()
push(int(a < b))
  • >=
b = pop()
a = pop()
push(int(a >= b))
  • <=
b = pop()
a = pop()
push(int(a >= b))

Arithmetic

  • + - sums up two elements on the top of the stack.
a = pop()
b = pop()
push(a + b)
  • - - subtracts the top of the stack from the element below.
a = pop()
b = pop()
push(b - a)
  • mod
a = pop()
b = pop()
push(b % a)

Bitwise

  • shr
a = pop()
b = pop()
push(b >> a)
  • shl
a = pop()
b = pop()
push(b << a)
  • bor
a = pop()
b = pop()
push(b | a)
  • band
a = pop()
b = pop()
push(b & a)

Control Flow

  • if <then-branch> else <else-branch> end - pops the element on top of the stack and if the element is not 0 executes the <then-branch>, otherwise <else-branch>.
  • while <condition> do <body> end - keeps executing both <condition> and <body> until <condition> produces 0 at the top of the stack. Checking the result of the <condition> removes it from the stack.

Memory

  • mem - pushes the address of the beginning of the memory where you can read and write onto the stack.
push(mem_addr)
  • . - store a given byte at the given address.
byte = pop()
addr = pop()
store(addr, byte)
  • , - load a byte from the given address.
addr = pop()
byte = load(addr)
push(byte)

System

  • syscall<n> - perform a syscall with n arguments where n is in range [0..6]. (syscall1, syscall2, etc)
syscall_number = pop()
<move syscall_number to the corresponding register>
for i in range(n):
    arg = pop()
    <move arg to i-th register according to the call convention>
<perform the syscall>

About

It's like Forth but in Python

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 100.0%