/ O

The O Programming Language

Flexibility to adapt to any idea

This is the specification for the O programming language compiler and standard libraries. O is a high-level object-oriented general purpose programming language currently in development. The main goal of the language is to provide a free (libre) and open source environment for software development that can cater to individuals, enterprises, and huge open source communities.

O is a work in progress. It is being actively worked on. You may view the source code and follow development progress on GitLab. You can also follow @Olang on the fediverse for occasional status updates.

Status Markers

Throughout the documentation are small status markers. These exist to show the current state of implementation for each part of the compiler or each feature of the language. This is what each marker means:

Concept. The item is just a concept and does not have any documented design.
Partial spec. The item has some specification, but it is incomplete.
Full spec. The item has complete specification, but has not yet been implemented.
Partial implementation. Work has been started in implementing the item, but it is not yet complete.
Full implementation. The item is fully implemented and functioning.
Complete. The item has a formal specification, documentation, and an implementation in a release of the applicable program or document.

Ultimate FizzBuzzSplat code example

Ultimate FizzBuzzSplat is a variation on FizzBuzz that adds 2 additional rules. The first is the addition of "Splat", which applies to 7 in the same way "Fizz" and "Buzz" apply to 3 and 5 respectively. What makes this "ultimate" is that you also add a "Fizz" for every digit 3 in the number, add a "Buzz" for every digit 5 in the number, and add a "Splat" for every digit 7 in the number. For instance, 33 is a multiple of 3, but also has 2 '3's in it, so you would say "FizzFizzFizz".

// importing std.io for the 'term' class to read and write to the terminal
import std.io ;
// declaring our program's entrypoint
entrypoint ufbs ;
// the constructor for the entrypoint is the entrypoint of the whole program
this() {
// the 'var' keyword can be used in place of a type to make use of type inference
// this overload of term.rdln ensures a safe read by asking again with a separate prompt on invalid input to parse the given type
var end = term.rdln<int>( "Enter a number: " , "No, a number: " ) ;
// we create an array literal with a range from 1 to our end point (inclusively) and pass it to a 'foreach' statement to iterate over each item
[ 1 .. end ] | foreach {
// 'foreach' makes the implicit 'each' variable available
// first we declare our counters for 'fizz', 'buzz', and 'splat' and increment them if applicable
var fizz = each % 3 ?? 0 ## 1 ; // if 'each % 3' is 0 then it's false, so increment on false
var buzz = each % 5 ?? 0 ## 1 ;
var splat = each % 7 ?? 0 ## 1 ;
// now we iterate over each character in the string of our number (what makes it "ultimate")
(string)each | foreach
// the 'each' here is each character in the string, shadowing the 'each' in the parent scope, but this doesn't matter as we don't need the parent scope's 'each'
// the '??' '##' syntax is a ternary if and may be used as a shorthand for 'if else' or in the same places a usual '?' ':' syntax will be used
each == '3' ?? fizz ++ ##
each == '5' ?? buzz ++ ##
each == '7' ?? splat ++ ;
// we didn't need the braces because there was only one statement as the body
// if any of 'fizz', 'buzz', and 'splat' are non-zero, then we print them
// the 'a *~ b' operator concatenates 'a' to itself 'b' times
if ( fizz + buzz + splat > 0 ) term.prln( "Fizz" *~ fizz , "Buzz" *~ buzz , "Splat" *~ splat ) ;
// else just print the number
else term.prln( each ) ;
}
}

That example is rather plain and simple in order to facilitate heavy commenting to describe language features. O can do much more though. This next example is exactly the same program, but written in a more functional style and takes only a single statement.

// members of the std.io.term are statically imported so we don't need to prefix methods with the class
import static std.io.term ;
entrypoint ufbs ;
this() // braces are not needed for single-statement method bodies
// we can get the output of term.rdln directly here rather than declaring a variable
[ 1 .. rdln<int>( "Enter a number: " , "No, a number: " ) ] |
// single-statement bodies don't need braces
foreach prln(
// declaring an anonymous method that takes 2 ints and returns an int (inferred)
fn ( int n , int f ) { return ( n % f ?? 0 ## 1 ) + ( (string)n | where ( each == ( (string)f )[0] ) ).len ; } |
// 'pipe' here is the method we piped in, so we can invoke it using pipe()
"Fizz" *~ pipe( each , 3 ) ~ "Buzz" *~ pipe( each , 5 ) ~ "Splat" *~ pipe( each , 7 ) |
// 'each' here is still in the scope of the foreach, so it is the number we're testing, which gets printed if the piped string has no length
pipe.len ?? pipe ## each ) ;

Contents