Version 2.0, 21 Dec 2005
SADOL is a functional, esoteric programming language invented by Martin "nooga" Gasperowicz in summer 2005. It is similar to LISP a bit. Specification of the language could be find in these addresses (it seems that they change fast so they can be inactive now):
But this document also contains full specification of the language mixed with detailed behavior of the BDSM2 interpreter, so you needn't original specification to learn it.
Main assumptions of SADOL are:
Nooga reached something really amazing - he designed language that is obfuscated and crazy as esoteric, but at the same time powerful and convenient to write even nontrivial programs.
BDSM means Badly Developed SADOL Machine. It is an interpreter of SADOL language. Main features:
BDSM in version 2 is now fully compatible with official nooga's language specification. It has been completely rewritten and contains new optimizations, especially copy-on-write on strings and lists.
Traditionally, first sample program prints out a string Hello world!
on the output.
!",212Hello World!
It consists of one expression that prints out a value on the console (!).
This value is a string (").
This string has length that is described by an integer number (,).
This number is two decimal digit length (2).
These digits are 12.
Then we have to write a 12 character length string.
It is Hello World!.
All whitespace characters (' ', '\r', '\n', '\t', '\v')
are ignored. It means that you can format your code the way you want to make
it more clear - or delete all whitespaces to make your program totally obfuscated.
Exception is where verbatim characters are expected (in ' and ") -
whitespaces are read as normal characters there.
Comments are all the characters inside curly brackets { }.
Comment cannot contain } inside.
{ My first program in SADOL }
! " , 2 12Hello World!
BDSM2 is executed from command line with one argument - name of a file with source code in SADOL
(I suggest extension .sad for that files).
Executed program gets input characters from the system console and puts output
characters on that console.
You can change this behavior and make BDSM2 processing input and output files
by using redirection on console input and output of the program to files.
This can be done by < and/or > system shell
operators - on both Windows and Linux. For example:
BDSM2.exe Source.sad < InputData.txt > OutputData.txt
There is a problem with end-of-line character on different platforms.
Under Linux, end of line character in text files is a single '\n'
character (decimal ASCII code 10).
Under Windows, it is represented by the sequence of two characters: "\r\n" (13 and 10).
So if you want to write a function that prints end of line at the output under Windows,
you should print two characters:
~ E 0 !"2
{ program end here }
There is also a problem that end of line characters can be handled in different
way when using < and > operators (redirecting console input/output to files).
Programs in SADOL cannot detect end-of-file state. So if you want your program to process some amount of information (eg. sort a list of numbers), you have to write the amount of data (eg. number count) at the beginning, before this data.
A value is a single data element that exists in the computer memory during execution of program. It can be passed somewhere (eg. to a function), returned from somewhere (eg. by an expression) or stored somewhere (eg. in a variable or as an element of other value).
Values are created and deleted automatically. There are no pointers or references, no memory leaks and no garbage collector. You also don't have to worry about passing, returning, storing and copying data by value, even if they are quite big (eg. long list, list of lists etc.). BDSM2 have copy-on-write optimization.
Data types in SADOL are dynamic. You don't have to declare that a function parameter or variable have particular type. There are four main types:
,3 666
.1 5
"3XXX
(elem1,elem2,elem3).
! $3 1 1 "1A
There is no separate boolean data type. Several operators require as arguments or return logical values. In general, you should think of boolean values as integer number that is true when greater or equal than 1.
There is not separate data type for a single ASCII character. Use one character length strings instead.
When instruction defines argument as converted into particular data type, it uses table below to obtain proper value. Conversions are done automatically.
| from integer | from double | from string | from list | |
|---|---|---|---|---|
| to integer | (none) | number is rounded | length of string (number of characters) | length of list (number of elements) |
| to double | numeric conversion | (none) | length of string (number of characters) | length of list (number of elements) |
| to string | conversion from integer to string | conversion from double to string | (none) | String containing (element1,element2,...)
|
| to list | one-element list | one-element list | one-element list | (none) |
| to boolean | true if >= 1 | true if >= 1.0 | true if not empty | true if not empty |
Some instructions require arguments to have value already known at compilation time. You can pass single values as these arguments, but you can also use instructions that can be optimized at compilation time (evaluated to single constants). Consider example:
!"+93Hello World!
SADOL as most of programming languages gives you ability to assign and read variables,
as well as define and call functions.
Variable and function names are case sensitive and can be identified by any single letter (a symbol) -
upper case A..Z for global and lower case a..z for local symbols.
Global symbols are common for entire program.
Wherever you use them, you refer to the same variable values or function code.
Local symbols exist in context of particular scope.
There is a main scope of the program (top level) and every function defined its own nested scope.
Local symbols a..z can be also used in the main scope of the program,
not only in functions' body.
There are no predefined variables or functions in SADOL. All possible variable/function names are at your command.
Variable is a place in memory for storing a value outside the main control flow (where values are passed as arguments for instructions or function calls and returned by them). You can assign its value and you can use this value in expressions. You can obtain a value of a variable that hasn't been assigned before - its default value is integer 0.
: A 3 { A := 3 }
: B + A 2 { B := A + 2 }
! B { output: 5 }
Function is a piece of code stored under particular name, that may be called, can take some arguments and can return value. You can define function by giving its name, number of arguments and expression to evaluate when executed (code of function body). You can then call this function with passing required number of arguments and you can use returned value. You cannot call function that hasn't been defined before.
~ F 1 { F is a function that takes 1 argument }
! #_0 { and prints its first argument on the output }
F 6 { Call this function with argument 6 }
Local variable exists only in the scope where it is assigned (from the first assignment to the end of this scope) and all the scopes called from it.
: a 1
: B 2
! a { output: 1 }
! B { output: 2 }
~ F 0 (6
! a { output: 1 }
! B { output: 2 }
: a 7
: B 8
! a { output: 7 }
! B { output: 8 }
F
! a { output: 1 }
! B { output: 8 }
Local function exists only in the scope where it is defined (from the definition to the end of the scope) and all nested scopes.
~ f 0
! "2f1
~ F 0
! "2F1
f { output: f1 }
F { output: F1 }
~ G 0 (5
f { output: f1 }
F { output: F1 }
~ f 0
! "2f2
{ cannot redefine global function!
~ F 0
! "2F2
}
f { output: f2 }
F { output: F1 }
G
f { output: f1 }
F { output: F1 }
To fully understand different shades of scopes, global and local variables and functions, variable assignments and function definitions, consider following examples:
! a { output: 0 }
: a 2 { assign local variable in main scope }
! a { output: 2 }
~ F 0 (3 { define a function }
! a { output: 2 }
: a 3 { assign local variable in function scope }
! a { output: 3 }
F { call this function }
! a { output: 2 }
Example of global function defined inside local function:
{ cannot call undefined function!
G
}
! X { output: 0 }
~ f 0 (2
~ G 0
!"1G
: X 7
G { output: G }
! X { output: 0 }
f
G { output: G }
! X { output: 7 }
Example showing difference between behavior of local variables and local functions:
~ g 0
! "2g1
: a 5
~ f 0 (2
g
! a
~ h 0 (3
~ g 0
! "2g2
: a 6
f
f { output: g1 5 }
h { output: g1 6 }
You can define a function with argument count equal to -1.
It means that it has variable argument count.
When called, there must be a number of arguments given every time and it must
be known in compilation time.
Inside the function, there is argument list _ available as in every
function, except you don't know the size of this list.
~ F -01 (2
: i 0
@ < i #_-01 (5
! i
! "3 =
! #_i
! "2
: i +i1
F4
1
.12
"5three
$4 "1f "1o "1u "1r
{ output:
0 = 1
1 = 0.2
2 = three
3 = (f,o,u,r)
}
There are several additional rules that you should be aware:
~f-01).
0..9
(none)
Integer
Use to express one-digit integer number constant.
No
Always
!1 !2 !3
, n d1..dn
Integer
Use to express integer number constant.
No
Always
: x ,3 666
. n d1..dn
Double
Use to express fractional part of real number constant. Returned value is sum of given digits, each multiplied by 10-n.
No
Always
! + ,210 .15 { output: 10.5 }
" n c1..cn
String
Use to express string constant.
No
Always
! "3Hi! { output: Hi! }
' o c
No
! '0A { output: 65 }
( n e1..en
Value of the last expression. If n = 0, returned value is integer 0.
Use to successively execute several commands. This is main control flow instruction. It is needed because SADOL is functional language. In imperative programming languages, you can express it by just typing commands.
Yes
Yes
~ F 1 (2 ! #_0 ! "1;
? c a b
If condition's value is true, returns value of a. Else returns value of b.
Conditional expression.
Yes
Yes
~ F 1 ! ? > #_0 ,210 "3>10 "4<=10 F,211 F5
@ c e
Value of last e execution. If there were no e executions it returns integer 0.
Loop instruction that executes e while c is true. Condition c is checked before executing e, so it can execute zero times.
Yes
Yes
: C 0
@ < C 5 (2
! C
: C + C 1
{ output: 01234 }
` s
Value returned by executed expression.
Executes given string as SADOL code and returns it's result (the result of last term).
s can contain sequence of expressions (as entire program source),
don't have to be single expression.
Executed code doesn't create new scope.
No
No
! ` ; 3
: l x
Assigned value - equal to x.
Assigns value x to l.
l can be any l-value, especially variable, list's element or string's character.
x is evaluated first.
No
No
: S "3abc
: #S1 "1X
! S { output: aXc }
+ a b
Sum of given values. For returned value type, see remarks.
Returns sum of given values. Here is a table of operations done and types of value returned according to types of given parameters:
| Integer | Double | String | List | |
|---|---|---|---|---|
| Integer | Integer Addition of integer numbers | Double Addition of double numbers | String String concatenation | List Merging lists |
| Double | Double Addition of double numbers | Double Addition of double numbers | String String concatenation | List Merging lists |
| String | String String concatenation | String String concatenation | String String concatenation | List Merging lists |
| List | List Merging lists | List Merging lists | List Merging lists | List Merging lists |
No
Yes
~ E 0 ! "2 !+ ,210 ,210 E !+ ,210 .225 E !+ ,210 "2ab E !+ ,210 $212 E !+ .225 ,210 E !+ .225 .225 E !+ .225 "2ab E !+ .225 $212 E !+ "2ab ,210 E !+ "2ab .225 E !+ "2ab "2ab E !+ "2ab $212 E !+ $212 ,210 E !+ $212 .225 E !+ $212 "2ab E !+ $212 $212 E
- a b * a b % a b
%, b cannot be 0.
Integer or double
Result of the computation.
Here is a table types of returned value according to types of given parameters:
| Integer | Double | String | List | |
|---|---|---|---|---|
| Integer | Integer | Double | (ERROR) | (ERROR) |
| Double | Double | Double | (ERROR) | (ERROR) |
| String | (ERROR) | (ERROR) | (ERROR) | (ERROR) |
| List | (ERROR) | (ERROR) | (ERROR) | (ERROR) |
No
Yes
! * - 2 % + 2 3 2 3 { output: 3 }
/ a b ^ a b \ a
/, b cannot be 0.
Double
No
Yes
: X / 9 2
! X { output: 4.5 }
! \ X { output: 4 }
$ n e1..en
List
Use to create list of given elements.
No
Yes
: A $2 1 2
: B $1 3
: C $0
! ++ABC { output: (1,2,3) }
# l n
n is proper index
l is string
l is list
n is -1
l is string
l is list
Indexing operator for extracting (or referencing) single elements of lists
and single characters of strings.
n is evaluated first.
Yes
Yes
: L $3 1 2 3
: C 0
! "1[
@ < C #L-01 (3
? > C 0
! "2,
0
! # L C
: C + C 1
! "1]
{ output: [1, 2, 3] }
] l x
l is string
l is list
Equal to l.
Pushes value x at the end of string or list l.
Original value l is modified in place, not only new value
is returned.
Yes
No
: L $0
! ]]] L 123 { output: (1,2,3) }
! L { output: (1,2,3) }
[ l
l is string
l is list
Pops last element (character) from given list (string).
This element is deleted from original value l (modified in place)
and returned by this instruction.
No
Yes
: A $3 1 2 3
@ A
! [ A
{ output: 321 }
~ c n e
Integer
Equal to 1.
Function definition.
No
No
~ F 1
(2
! ",211Argument =
! #_0
F ,210 { output: Argument = 10 }
F "3abc { output: Argument = abc }
_
(none)
List
List of current function's arguments.
When used in global scope, returns empty list.
Yes
No
~ F -01
!_
F1 1 { output: (1) }
F2 1 2 { output: (1,2) }
F3 1 2 3 { output: (1,2,3) }
= a b < a b > a b
Boolean
Result of comparison
Compares whether first value is equal / less / greater than the second. Here is a table of operations done according to types of given parameters:
| Integer | Double | String | List | |
|---|---|---|---|---|
| Integer | Integer number comparison | Double number comparison | Lexigraphic string comparison | Lexigraphic list comparison |
| Double | Double number comparison | Double number comparison | Lexigraphic string comparison | Lexigraphic list comparison |
| String | Lexigraphic string comparison | Lexigraphic string comparison | Lexigraphic string comparison | Lexigraphic list comparison |
| List | Lexigraphic list comparison | Lexigraphic list comparison | Lexigraphic list comparison | Lexigraphic list comparison |
No
Yes
: A 1 ? > A 0 ! "2>0 0 ? < A 0 ! "2<0 0 ? = A 0 ! "2=0 0
& a b | a b
Boolean
Logical AND / OR operation on given boolean values.
Expression a is evaluated first.
If its result determines result of entire instruction, expression b is not evaluated.
No
Yes
: A 1 : B 2 ? & >A0 >B0 !",211A and B > 0 0
! x
Equal to x.
Prints value of x on the console output.
(element1,element2,...).
No
No
! "6Value= ! 7
; t
Value read from console input.
Read character(s) from console input, converts it to needed data type
and returns its value.
It always waits for pressing Enter on keyboard, even if only one character is needed.
No
No
! ",235Do you really want to quit? (Y/n): : A ;0 ? | =A"1Y =A"1y :B1 :B0 !B
In general, BDSM2 i brand-new, rewritten from scratch and incompatible with BDSM1. It is now compatible with official nooga's specification of SADOL language. Many optimizations were also made.
Simple programs in SADOL written for BDSM1 should also work in BDSM2.
Main change that you should make is replacing every ;0
to ;1, ;2 or ;3 - according to desired
data type.
] returns modified string or list, not appended element.
\ is means floor, not root.
' works in different way.
; works in different way.
a..z are local variables and functions.
Adam Sawicki
Contact:
mailto:regedit_DEL_@risp.pl (delete _DEL_)
http://www.regedit.risp.pl/
At the end, I want to apologize for my poor English. It is not my native language and I have been writing this document fast so please forgive me my mistakes :)