kleinesfilmroellchen / sof-language

The Stack with Objects and Functions Programming Language, a pure stack-based reverse-polish-notation functional and object-oriented experimental programming language.
https://kleinesfilmroellchen.github.io/sof-language/
GNU General Public License v3.0
11 stars 0 forks source link
compiler compiler-construction interpreter java personal-programming-language personal-project programming-language sof sof-interpreter

CodeQL Gradle CI & Tests codecov documentation

SOF - Stack with Objects and Functions

An experimental fully stack-based reverse-polish-notation functional and object-oriented programming language concieved and implemented by kleinesfilmröllchen.

SOF is to be pronounced 'ess-oh-eff' in English or /əs.ʊu.ˈəf/ in IPA. If you want to make me angry, you can also pronounce it 'sohf'.

This is an experimental programming language. If you cause a nuclear war and the inevitable destruction of mankind by using this software, I am not to blame.

SOF is written in Java 16 and requires org.reflections and java.logging to run. It leverages the module system (you may use it in your project as well!) and uses JUnit Jupiter for testing the codebase. (See above for coverage reports as of the latest commit.)

Installation and CLI Usage

This is a Gradle 7 project using the Java Application plugin with the module system and JUnit Jupiter tests. The usual Gradle tasks for these situations exist and have not been renamed/added to. As a quick reference: Use gradle build to run the full build including tests. Run gradle test to run the tests, and gradle coverage for tests and coverage (reports are in build/jcc-report/test/html). Use gradle javadoc to build the javadoc. All building happens into the build/ subfolders.

Use gradle run to execute the SOF CLI. However, Gradles obnoxious build output will obscure a bunch of the program output. Therefore, you should use gradle install and then run the binaries from build/install/sof-language. This works on Windows and Linux.

The command line tool currently supports the following arguments and options (taken from help output):

sof - Interpreter for Stack with Objects and       
      Functions (SOF) Programming Language.
usage: sof [-hvdpP] [-c COMMAND]
           FILENAME [...FILENAMES]

positional arguments:
   filename  Path to a file to be read and
             executed. Can be a list of files that
             are executed in order.

options:
   --help, -h
             Display this help message and exit.
   --version, -v
             Display version information and exit.
   -d        Execute in debug mode. Read the manual
             for more information.
   -p        Run the preprocessor and exit.
   -P        Do not run the preprocessor before
             executing the input file(s).
   --command, -c COMMAND
             Execute COMMAND and exit.
   --performance
             Run performance tests and show results

When used without execution-starting arguments (-c
or filename), sof is started in interactive mode.

Quit the program with ^C.

A basic Visual Studio language support extension has been written, which can be found on the marketplace and here on github.

The documentation is browsable in-repo in docs or online here and can be built into website-compatible HTML with mdbook:

cd docs; mdbook build

1. What is SOF?

"Hello World!" writeln

SOF, the (ugly but useful & pronouncible) acronym for Stack with Objects and Functions, is a programming language first concieved in June of 2019 by kleinesfilmröllchen. It has exactly one goal, which is also its name:

Create a pure stack-based programming language with object-oriented and functional capabilities.

This means that SOF has the following main features:

2. Some Sample Programs

To get a taste of SOF, here are several sample programs written in SOF. Also checkout the examples folder.

# fibbonachi

{
    n def
    0 i def
    1 x def 1 y def 1 z def
    {
        z .
        x . y . + z def # z := x + y
        y . x def # x := y
        z . y def # y := z
        writeln # z old
        i . 1 + i def
    } { i . n . < } while
} 1 function fib def

"enter a number: " write input convert:int : fib :

# alternative function definition: heavier stack use, runs faster

{
    1 x def 0 y def # counters, z is only defined later
    {
        y . dup x . + z def # z = y + x,  y (old) on the stack
        z . y def x def # y = z, x = y (old)
        z . writeln # write z, counter on the stack
    } { 1 - dup 0 > } while # while counter > 0
} 1 function fib def
# factorial

{
    dup dup # n*3 on the stack
    { 1 return } swap 2 < if # return 1 if n < 2, n*2 on the stack
    1 - fact : * return # return factorial of n-1 times n
} 1 function fact def

"enter a number: " write input convert:int : fact : writeln
# play a guessing game
random:int number def
0 guess def
{
    input convert:int : guess def
    switch:: {
        "You are correct!" writeln
    } { number . guess . = }
    {
        "You are too low!" writeln
    } { number . guess . < }
    {
        "You are too high!" writeln
    } switch
} { guess . number . /= } while
# compute the sum of the first one hundred natural numbers, using a loop and a function

100 N def

# loop
1 i def
0 result def
{
    result . i . + result def
    i . 1 + i def
} { i . N . <= } while

"The result (loop) is: " result . cat writeln

# function
{
    n def # arg1
    # gauss formula
    n . n . 1 + * 2 / return
} 1 function sum def

N . sum :
"The result (function) is: " swap cat writeln 

3. Basic Principles of SOF

An SOF program consist of a list of "tokens", input elements that can be one of three basic syntatic types:

  1. Basic token: Tokens that don't contain spaces.
  2. String literal token: Tokens that represent string literals; these start with a double quote " and end with another one.
  3. Code block token: Tokens that represent executable blocks of code and contain other tokens; these start with a open curly bracket { and end with a close curly bracket }.

Tokens are separated by at least one arbitrary whitespace character, e.g. space, newline, tab. This means that SOF is extremely tolerant in terms of formatting, but even non-word tokens like arithmetic operators have to be separated visually.

Every token can be seen as an action that operates on the stack. Some basic tokens include:

4. The Minimalism of SOF - A Personal Note

SOF is a minimalist language.

SOF is a tool for understanding programming languages. It is very usable, the learning curve is not very steep regardless of whether you have programming experience or not, and it is turing-complete and universal. At the same time, it is so simple that I can write its interpreter or even a compiler without knowing how 'real' compilers work or how to write a 'real' compiler. Even very primitive environments should be able to parse and run SOF.

Just to be pretentious; Antoine de Saint-Exupéry is famously quoted on

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.

I think that SOF is an excellent example of this. I cannot currently think of anything that you can take away from SOF without making it inferior in capability (or, at least, severely less usable, like removing the function PT would do, for example). It is very surprising indeed that such a simple thing as a reverse-polish notation language with three token types and literally less than 30 lines of syntax definition could be as feature-rich and capable as any other modern programming language, even if with odd syntax and inferior speed. (The latter is down to me being simply not capable of writing an efficient interpreter or any sort of compiler. PostScript and Forth prove that postfix languages can be extremely performant.)

5. Running on SerenityOS

SOF kind of runs on SerenityOS. There's a helper script in the repo for the time being, and if you want to try and get it to run, please get in touch.