Stlang

From Esolang
Jump to navigation Jump to search

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 ;))