Selt

From Esolang
Jump to navigation Jump to search

Selt is a self-modifying esoteric programming language created by User:Zip.

Structure

Each Selt program consists of a series of commands and labels; each line has one of each. Labels and commands are seperated by colons (:). If a line contains no label, then the label of that line is the empty string by default.

Labels

Labels can contain any character except a colon. Additionally, any whitespace (tabs or spaces) before a label name is ignored. A label can be repeated on multiple lines, however this will result in only the first instance of the label being accessible.

Commands

A command consists of a series of terms and operators. A term is any sequence of characters, and may be broken by operators or spaces. In order to prevent a term being broken up, a backslash (\) may be used to escape a character. Each command contains an instruction (which is just a term at the beginning of the command). Each instruction takes a certain number of operands, which may be either terms or expressions. Expressions are combinations of terms and operators, which denote a calculation to perform at runtime. Expressions and terms are fully interchangeable.

Instructions

Form Name Description
print [expr] Print Prints the value of [expr]
println [expr] Print Line Prints the value of [expr], followed by a newline (U+000A)
goto [expr] Goto Jumps execution to the label named the value of the expression. If the label does not exist, an error message is given and execution is halted.
call [expr] Call Calls a subroutine at a label. A return instruction may be used to return back to the previous call.
return Return Returns back to the previously ran call instruction. If no call instructions were executed previously, then execution halts.
[expr A] = [expr B] Assignment Sets the value at the label [expr A] to [expr B]

Binary Operators

Binary operators take two arguments; one on the left, and one on the right.

Operator Name Description
+ Add Interprets both arguments as integers, adds them, and returns the result. If either side can not be interpreted as an integer, then an error will be thrown an execution will halt. (This is true of all arithmetic operations (Those being + - / * %))
- Subtract Interprets both arguments as integers, subtracts them, and returns the result.
* Multiply Interprets both arguments as integers, multiplies them, and returns the result.
/ Divide Interprets both arguments as integers, divides them, and returns the result.
% Multiply Interprets both arguments as integers, modulos them, and returns the result.
~ Concatenate Concatenates the value of both arguments.
. Index Takes the second argument, and takes that index of the first argument. If the index is out of bounds, then an error is given and execution halts.
== Equals Returns 1 if both sides are equal, 0 if not.
!= Not Equals Reverse of equals
> Greater Than Interprets both arguments as integers, then returns 1 if the left side is greater than the right side, otherwise returns 0. If either side can not be interpreted as an integer, then an error is given and execution halts. (This is true of all comparison operators (those being > < >= <=))
< Less Than Interprets both arguments as integers, then returns 1 if the left side is less than the right side, otherwise returns 0.
>= Greater Than or Equal Interprets both arguments as integers, then returns 1 if the left side is greater than or equal to the right side, otherwise returns 0.
<= Less Than or Equal Interprets both arguments as integers, then returns 1 if the left side is lesser than or equal to the right side, otherwise returns 0.
&& And Returns 1 only if both sides are 1, and 0 otherwise. (Note that this is not a short-circuit operator; it will always evaluate both sides.)
|| Or Returns 1 only if both sides are 1, 0 otherwise. (Likewise, this is not a short-circuit operator.)

Unary Operators

Unary operators only take one argument, placed to the right of the operator

Operator Name Description
! Not If the argument is 1, returns 0, otherwise returns 1.
@ At Returns the value at a label. If the label does not exist, execution halts and an error is thrown. (likewise with &#amp;)
& Line Gets the line of a label.
| Deline Gets the text at a given line. (not including the label at that line.)
? Length Returns the length of the argument.

Operator Precedence

Below is a table of operator precedence. A higher precedence means that an expression utilizing that operator will be evaluated before a neighboring expression. If two operators have the same precedence, then they're evaluated left-to-right.

Operator(s) Precedence
@ & | ! . ? 7
|| 6
&& 5
== != < <= > >= 4
* / % 3
+ - 2
~ 1

Miscellaneous

  • The label stdin is special; when you try to read a value from that label it reads a line from standard input (no trailing newline is added.)
  • The grave symbol (`) is used to represent an empty string. This can be escaped with a backslash like any other character.

Programming in Selt

Selt is a self-modifying language, that is when you run a selt program, the code itself is being modified as it runs. The assignment instruction doesn't assign variables, it assigns labels. This means that in order to store a value, you store it in the code itself. To see what I mean, consider the following code:

x:
x = 5
println @x

The first line contains a label called x. There is no instruction on this label, so nothing happens when this line is read. On the next line, we read x = 5. This sets the value at the label x to 5. One very important thing to note is that since all that is being done is manipulating text, all values are a string. We then run println @x, which gets the value at the label x and prints it. In theory, we could jump back to the label x and run it as code, however that would just cause an error.

Escaping

You have to escape characters a lot in Selt. This is because strings are not quoted; since it's the only datatype in Selt it would just get overly annoying to do. Instead, if you want a space or a reserved operator in Selt, you just escape it with a backslash. To demonstrate, here's the Hello, World! program in Selt:

println Hello,\ World\!

You can see there's a few backslashes in there. The first backslash is to escape a space. If that one wasn't there, then this would be interpreted as two terms following one another, which is not what we want (and would cause an error). The next backslash is to escape the ! operator, which would otherwise be interpreted as, well, an operator, and not as part of our string.

Loops

Loops can be done using the goto command. So, let's say you want to repeatedly increment a number and print it out. That program could be achieved by the following code:

goto loop
n:0
loop:n = @n+1
     goto loop

Note the goto loop at the start. This makes sure that we skip over the code after it, because if that got executed, it would give an error (since 0 is not a valid command). In the loop, we assign the label n to @n+1. This takes the value at the label n and adds one to it.

Conditionals

You may notice that Selt does not have a conditional instruction. However, what selt does have is conditional operators. And, since Selt has the concatenation operator and allows you to goto an expression, you can use this to jump to one label if a condition is false, and another if a condition is true. Here's a following program that detects if the user typed "Hello" or not:

goto say~(@stdin == Hello)
say0:println You\ did\ not\ type\ hello
return
say1:println You\ did\ type\ hello

Note the return at line 3. This makes sure that execution does not continue down to say1 by halting the program. Now, with conditionals and loops, we can create a Truth Machine in Selt:

goto loop~(@stdin == 1)
loop1:print 1
goto loop1
loop0:print 0

Examples

The following is a list of example programs in Selt

99 bottles of beer

# https://esolangs.org/wiki/99_bottles_of_beer
loop:
  println @bottles~\ bottles\ of\ beer\ on\ the\ wall,
  println @bottles~\ bottles\ of\ beer\.
  println Take\ one\ down,\ pass\ it\ around,
  bottles = @bottles-1
  goto cont~(@bottles > 1)
  cont1:println @bottles~\ bottles\ of\ beer\ on\ the\ wall\.
  println `
  goto loop
cont0:
  println 1\ bottle\ of\ beer\ on\ the\ wall\.
  println `
  println 1\ bottle\ of\ beer\ on\ the\ wall,
  println 1\ bottle\ of\ beer\.
  println Take\ one\ down,\ pass\ it\ around,
  println No\ more\ bottles\ of\ beer\ on\ the\ wall\.
return
bottles:99

Alphabet

Takes an index, and returns that letter of the alphabet (0-indexed)

# given a number, this prints that letter of the alphabet (zero-indexed)
goto start
alphabet:a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
start:println |(&alphabet+@stdin)

Cat by Char

A simple program to demonstrate looping through a string

# prints out the input char by char
var = @stdin
loop1:
  print @var.@index
  index = @index+1
  goto loop~(@index < ?@var)
loop0: return
var:
index:0

Cat

# prints its input
start:println @stdin
goto start

Deadfish Interpreter

# https://esolangs.org/wiki/Deadfish
loop:
  print \>\>\ 
  input = @stdin
  index = 0
  goto inloop~(@input != `) # jump to end when input is empty
  inloop1:
    goto @input.@index # go to the label corresponding to each command
    i:val = @val+1
    goto end
    d:val = @val-1
    goto end
    s:val = @val*@val
    goto end
    o:println @val
    goto end
    end:goto reset~((@val == \-1) || (@val == 256))
    reset1:val = 0
    reset0:
    index = @index+1
    goto inloop~(@index < ?@input)
  inloop0: goto loop
index:0
val:0
input:

Code Editor

# a simple program that lets you edit and execute selt code
goto start
index:
line:
# changeable, just add more lines to the end of the program
lines:20
start:print \?
goto @stdin
edit:
  println Line
  line = @stdin
  println Text
  code~@line = @stdin
  goto start
view:
  index = 0
  viewloop0:
    println @(code~@index)
    index = @index+1
    goto viewloop~(@index >= @lines)
  viewloop1:goto start
run:
code0:
code1:
code2:
code3:
code4:
code5:
code6:
code7:
code8:
code9:
code10:
code11:
code12:
code13:
code14:
code15:
code16:
code17:
code18:
code19:

Hello, World!

# prints Hello, World!
println Hello,\ World\!

Quine

print |1

Truth Machine

# takes input, if 0 it prints 0 and halts. if 1, it prints 1 indefinitely
goto branch~@stdin
branch1:print 1
goto branch1
branch0:print 0

Brainfuck Interpreter

loop:
  print BF\>\ 
  code = @stdin
  goto end~(@code == `)
  end0:
  ip = 0
  execloop1:
    goto i~(@code.@ip)
    i+:tape~@ptr = @(tape~@ptr)+1
    goto execend
    i-:tape~@ptr = @(tape~@ptr)-1
    goto execend
    i>:ptr = @ptr+1
    goto execend
    i<:ptr = @ptr-1
    goto execend
    i.:goto ch~((@(tape~@ptr)) == 10)
    ch1: println `
    goto execend
    ch0: print @ascii.(@(tape~@ptr))
    goto execend
    i[:goto lbrack~(@(tape~@ptr) == 0)
    lbrack1:
      brack = 1
      lbrack_loop1:
        ip = @ip+1
        goto lbrack_~(@code.@ip)
        lbrack_[: brack = @brack+1
        goto lbrack_\+
        lbrack_]: brack = @brack-1
        lbrack_+:
        lbrack_-:
        lbrack_>:
        lbrack_<:
        lbrack_.:
        lbrack_,:goto lbrack_loop~(@brack > 0)
      lbrack_loop0: goto execend
    i]:goto rbrack~(@(tape~@ptr) != 0)
    rbrack1:
      brack = 1
      rbrack_loop1:
        ip = @ip-1
        goto rbrack_~(@code.@ip)
        rbrack_[: brack = @brack-1
        goto rbrack_\+
        rbrack_]: brack = @brack+1
        rbrack_+:
        rbrack_-:
        rbrack_>:
        rbrack_<:
        rbrack_.:
        rbrack_,:goto rbrack_loop~(@brack > 0)
      rbrack_loop0: goto execend
    lbrack0:
    rbrack0:
    execend:
    ip = @ip+1
    goto execloop~(@ip < ?@code)
  execloop0:
    println `
  end1:
    goto loop
code:
ip:
brack:
ptr:0
ascii:         	                       !"#$%&'()*+,-./0123456789 ;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ 
tape0:0
tape1:0
tape2:0
tape3:0
tape4:0
tape5:0
tape6:0
tape7:0
tape8:0
tape9:0
tape10:0
tape11:0
tape12:0
tape13:0
tape14:0
tape15:0
tape16:0
tape17:0
tape18:0
tape19:0
tape20:0
tape21:0
tape22:0
tape23:0
tape24:0
tape25:0
tape26:0
tape27:0
tape28:0
tape29:0
tape30:0
tape31:0

Interpreter

The official interpreter for Selt is available Here, and is also packaged in DELIC