Stlang
Stlang is a stack-based esoteric programming language invented by User:Feuermonster in 2012. The main idea is to create a stack-based esoteric language with a very sophisticated standard library which outshines other major languages' standard libraries. Also, due to possible use in golfing, often-used function names must have a short, symbolic alias. Stlang supports anonymous functions. But most importantly: The spirit of Stlang is to implement lots of features in very weird and esoteric ways no other language or sane person would implement them with.
Builtin functions
Note: Since Stlang is stack based, "return" means "push the result on to the stack". This also applies to things like "removes element from list", etc.
Function | Alias | Parameters | Description |
---|---|---|---|
add | +
|
a, b | Returns a + b |
sub | -
|
a, b | Returns a - b |
mul | * =
|
a, b | Returns a * b |
div | / or //
|
a, b | Returns a / b |
map | m
|
fn, ls | Applies the function pointed to by fn to each element in ls and collects the return values in a list. |
dup | ++ or <+
|
a | Duplicates the top-most element of the stack |
swp | <>
|
a,b | Swaps the two top-most elements of the stack |
flz | n | Creates a list with n zero elements | |
red | r
|
fn, ls | Applies the function pointed to by fn to the first two elements of ls, stores the result, and applies the function to this last result and the next element in ls. |
app | :
|
ls, e | Appends the element e to the list ls. |
sum | /S
|
ls | Returns the sum of all elements in ls. |
prd | /P
|
ls | Returns the product of all elements in ls. |
sqrt | /v
|
a | Returns the square-root of a. |
len | l
|
ls | Returns the length of ls. |
nub | n
|
ls | Removes all duplicate elements from ls. |
rev | <-
|
ls | Reverses ls. |
ls | []
|
Returns an empty list. | |
fac | !
|
a | Returns the factorial of a |
pow | ** or /*
|
a,b | Returns a ** b |
ln | a | Returns the natural logarithm of a | |
log | a | Returns the 10-logarithm of a | |
e | Returns the number e | ||
pi | Returns the number pi | ||
sin | a | Returns sin(a) | |
sinh | a | Returns sinh(a) | |
asin | a | Returns asin(a) | |
asinh | a | Returns asinh(a) | |
cos | a | Returns cos(a) | |
cosh | a | Returns cosh(a) | |
acos | a | Returns acos(a) | |
acosh | a | Returns acosh(a) | |
tan | a | Returns tan(a) | |
tanh | a | Returns tanh(a) | |
atan | a | Returns atan(a) | |
atanh | a | Returns atanh(a) | |
flr | a | Returns floor(a) | |
ceil | a | Returns ceil(a) | |
deg | a | Converts from radians to degrees. | |
rad | a | Converts from degrees to radians. | |
head | ;h
|
ls | Returns the first element of ls. |
tail | ;t
|
ls | Returns all but the first element of ls. |
last | ;l
|
ls | Returns the last element of ls. |
init | ;i
|
ls | Returns all but the last element of ls. |
pair | (,) or (,
|
a,b | Returns the pair (a,b) |
zipWith | <,> or ;Z
|
fn,as,bs | Zips the lists as and bs using the function fn. (Google zip if you do not know what zipping lists means). |
unzip | ls | >,< or ;U
|
Counterpart of zip. Undoes what {pair} zipWith would do. Returns a pair of lists.
|
take | n, ls | ;<
|
Returns the first n elements of ls. |
drop | n, ls | ;>
|
Removes the first n element of ls. |
unfinished. More documentation is coming, of course.
Details
As mentioned: Stlang is stack based. Everything which is not a function call is pushed on to the stack. Functions then take values from the stack, do their stuff, and push the result back on to the stack.
Stlang also supports anonymous functions (lambdas) with the {}-Notation. {5 add}
is an anonymous function which obviously adds 5. What Stlang does with anonymous functions is, it creates a new standalone function with a random name for that anonymous function and replaces the anonymous function inside the code with a call to this randomly named function. There are functions to actually access the name of the created function (me
is replaced with the name of the current anonymous function, and parent
or ^-
is replaced with the name of the parent anonymous function) but these are considered to be especially esoteric ;).
Stlang also performs a little optimization. Let's see an example:
fn myRange []{<> ++ 0 >{1 - ++ -3 ^^ <> : ^-}{$ <-}?}; efn fn main 10 myRange efn
is converted to
fn 9C36B5A81FG724E0 <> ++ 0 > '94C7BF2518E03DA6 'GB1C6785D3024FA9 ? efn fn myRange [] '9C36B5A81FG724E0 ; efn fn main 10 myRange efn fn GB1C6785D3024FA9 $ <- efn fn 94C7BF2518E03DA6 1 - ++ -3 ^^ <> : 9C36B5A81FG724E0 efn
These weird function names are the anonymous functions. This is further optimized through inlining to
fn main 10 [] <> ++ 0 > '94C7BF2518E03DA6 'GB1C6785D3024FA9 ? efn fn GB1C6785D3024FA9 $ <- efn fn 94C7BF2518E03DA6 1 - ++ -3 ^^ <> : <> ++ 0 > '94C7BF2518E03DA6 'GB1C6785D3024FA9 ? efn
And that's about it ;)
Hello, World!
The following code demonstrates various ways to achieve the goal of greeting the world.
fn main way1 way2 way3 way4 way5 way6 efn fn way1 'Hello, \s + 'World! + @ efn fn way2 'Hello, <o \s <o 'World! <o \n <o efn fn way3 \n 'World! \s 'Hello, <o <o <o <o efn fn way4 {<o} [] 'Hello, : \s : 'World! : \n : m efn fn way5 [] 'Hello, : \s : 'World! : _ @ efn fn way6 [] 'Hello, : \s : 'World! : \n : <- # <o <o <o <o efn
Syntax
A function is declared by fn
followed by a space and the name of the function. A function declaration is terminated by efn
.
Normal functions can not be nested. Anonymous function are declared between {
and }
. Anonymous functions can
be nested. A function can be referenced by '
followed by the name of the function. When a program is launched, its main method is executed. Function calls
and values are usually separated by spaces (if not in golfing mode).
Comments
There are no real comments, but we still can write comments like this:
fn comment {This is a comment, but it's actually just code which is never executed} $ doStuff efn
Strings
There are no real strings, but it is possible to abuse that function references are implemented as strings therefore
fn \o/ 'String <0 efn
will output String
to standard out. I think it is obvious that this may have some esoteric side effects if you are careful.
fn \o/ 'String ++ ; <0 efn fn String 0 $ efn
This will call the function String
and then output String
.
Variables
There are no real variables (you should know this sentence by now ;)), but it is possible to create functions on the fly. The following examples demonstrates
this by creating a function test
on the fly and call it:
fn main 'test [] ''Hello : '@ : ^F test efn
This can be used for storing values as the following code demonstrates:
fn main {Initialize myGlobalVar with the value 1336. Remember: Code is string -> We have to store 1336 as string. We use (i to convert the float to int, as per default all numbers are floats. } $ 'myGlobalVar [] '1336 : '(i : ^F myGlobalVar @ {This will print 1336}$ doSomething myGlobalVar @ {Somehow myGlobalVar was magically incremented ;)}$ efn fn doSomething {Load myGlobalVar add 1 to it and store it back again} $ myGlobalVar .1+(s[]<>: '(i : 'myGlobalVar <> ^F efn
Pattern matching
Stlang also supports something like pattern matching. Functions can match against the top-most element of the stack.
The pattern to match is followed by the name of the function and a :
. Here is an example:
fn main 0 isZero @ efn fn isZero ?0 efn fn isZero:0 ?1 efn fn isZero:0.0 ?1 efn
Another example:
fn main 'John checkName 'Lisa checkName 'Dylan checkName efn fn checkName 'I \s + 'don't + \s + 'know + \s + <> + @ efn fn checkName:John 'I \s + 'like + \s + <> + @ efn fn checkName:Dylan 'I \s + 'hate + \s + <> + @ efn
which produces the following output:
I like John I don't know Lisa I hate Dylan
Golfing
There is a special golfing mode for a sequence of function calls. This mode is entered with a dot (.
) and left with a space.
This 11 characters long program reads lines in an infinite loop from the console and reverses them.
M ./r<-` \
Without the golfing mode this program has to be written as:
M /r <- ` \
In clear code the same program is:
fn main readln rev show main efn
OOP
Stlang also supports some sort of object oriented programming. Classes can be defined as well as some sort of inheritance. Classes can inherit from multiple classes and from each other.
Classes
class Animal fn makeSound &sound show efn prop sound 'Miau eclass fn main 'Animal new makeSound 'sound 'Woof modprop makeSound efn
In this example a class named Animal is defined and an object of that class is created in main by the new function. modprop is a function to modify properties.
Properties can be accessed through &
followed by the name of the property.
Casts and Inheritance
class Person fn greet 'Hello, out space out &Name show efn prop Name 'Peter eclass class Manager fn greet 'Greetings, out space out &Name show efn eclass inherit Manager Person fn main 'Person new greet pop 'Manager new greet pop 'Manager new 'Name 'John modprop greet 'Person castTo greet pop efn
Objects of classes can be casted to other classes with the castTo
function. Any class can be casted to any other class!
class Foo fn do 'Foo show efn eclass class Bar fn do 'Bar show efn eclass class Baz fn foobar 'Baz show efn eclass class Buz eclass inherit Buz Baz inherit Buz Bar class A fn a 'A show efn eclass class B fn b 'B show efn eclass inherit A B inherit B A fn main 'Foo new 'Bar castTo do pop 'Buz new foobar do 'Foo castTo do pop 'A new a b pop 'B new a b pop efn
Classes can inherit from multiple classes and even a class A can inherit from B and B can inherit from A.
class Tester fn addTest pop add efn fn saySomething 'something show efn eclass fn main 'Tester new saySomething 5 6 that addTest swp show saySomething efn
The that function can be used to get the latest object back on top of the stack (no matter where on the stack it is). This is sometimes necessary if you want to pass parameters to a member function.
Constructors/Destructors
Classes can implement the functions "con" (constructor) and "decon" (deconstructor) which are called automatically on object construction or deconstruction.
Internals
An object is somehow an instance of a class. A class holds function definitions and properties with their default value. The new function creates an object based on its class definition and pushes that object on to the stack. If a function is called which is neither a builtin function nor a function defined in the global scope Stlang checks if the topmost value on the stack is an object and if it is, it calls the function of the corresponding class. After a call to a member function the topmost value of the stack is always the object. Property accessing works analog to this. To access a property of an object that object must be on top.
Examples
Truth-machine
Clear-text version
fn main readln toint 1 eq { { pop false } { 1 toint show } 0 until } { 0 toint show } cif efn
Golfed version
M ./r(i1=={{.$?0}{.1(i@}.0U}{.0(i@}? \
OOP version
class 0 fn con 0 toint show efn eclass class 1 fn con { pop false } { 1 toint show } 0 until efn eclass fn main readln new efn
Golfed OOP version
Cc 0 fn con .0(i@ \ cC Cc 1 fn con{.$?0}{.1(i@}.0U \ cC M ./rN \
99 bottles of beer
fn main 99 (i) strophe efn fn strophe <+ '_bottles_of_beer_on_the_wall, '_ .\s/R<><o@ <+ '_bottles_of_beer. '_ .\s/R<><o@ 'Take_one_down,_pass_it_around, '_ .\s/R@ .1-(i<+ '_bottles_of_beer_on_the_wall. '_ .\s/R<><o@ \n <o strophe efn fn strophe:1 <+ '_bottle_of_beer_on_the_wall, '_ .\s/R<><o@ <+ '_bottle_of_beer. '_ .\s/R<><o@ 'Take_one_down,_pass_it_around, '_ .\s/R@ $ 'No_more_bottles_of_beer_on_the_wall. '_ .\s/R@ efn
Others
Find the first number 2^n
greater than 100:
fn \o/{100 >}{2 *}2 -> @ efn
Read comma seperated values from the console and print the sum of all numbers (infinite loop):
fn \o/{+}{(i)}/r ', ;; m r @ \o/ efn
Print the 11th fibonacci number:
M 11 fib @ \ fn fib .<+1-"<>2-"+ \ fn fib:0.0 \ fn fib:1.0 \
Print the 11th fibonacci number in clear, commented, well formated code:
fn main 11 fib show efn fn fib {fib n = (fib (n-1)) + (fib (n-2))} pop dup 1 sub fib swp 2 sub fib add efn fn fib:0.0 {fib 0 = 0} pop efn fn fib:1.0 {fib 1 = 1} pop efn
Implementation
Not available anymore.
Problems
- Ugly code (of course)
- evalFn is recursive so the max recursion depth is reached very fast (in that case, python does not terminate but yield the weirdest results ;))