Sike

From Esolang
Jump to navigation Jump to search

Sike [ˈsi.ke] (SEE-kay) is a deque-based esoteric programming language created by User:Zip, based on the idea of both code and and data sharing the same collection. Instructions are shifted (i.e. dequeued) from the front of the deque, and data is popped from the back of the deque. The name is from toki pona and means something like circle or cycle.

Syntax and Types

Sike has fairly traditional syntax similar to many stack-based languages. The program is a sequence of whitespace-delimited tokens. Tokens can be either a number, a character, or a word. All tokens, once parsed, become the program's initial deque.

Numbers

Numbers are 64-bit signed integers. They optionally begin with a minus -, and then contain a sequence of digits. Examples:

0
20
-1234

Characters

Characters represent a single unicode codepoint. They start with quote ' followed optionally by any character (encoded in UTF-8). If no character is specified, the character represents space (U+0020). Alternatively, after the quote a character may contain u followed by a hexadecimal number specifying a unicode codepoint. Examples:

'A   # LATIN CAPITAL LETTER A  U+0041
'    # SPACE                   U+0020
'u0A # LINE FEED               U+000A

Words

Tokens that do not start with quote and can not be parsed as a number are parsed as words. There is a limited number of valid words, and new words can not be defined. Examples:

dup
swap
unpack

Packs

In addition, there are packs which are groups of tokens surrounded by brackets []. There does not need to be whitespace between brackets and surrounding tokens. Examples:

# Pack containing 1, 2, and 3
[ 1 2 3 ]
[1 2 3]
# Empty pack
[]
[ ]
# Packs may be nested
[ 1 2 [ 3 4 ] ]

Keep tokens

A token may also be marked keep, meaning that after evaluation it is to be pushed to the back of the deque. Keep tokens begin with a dot .:

# Keep tokens of different types
.1
.[ 1 2 3 ]
.'A
.swap

Comments

Comments start with # and end at the end of the line. Examples:

# hello
pack unpack # do nothing

Execution

The program continuously runs cycles until the deque is empty. Each cycle, a value is shifted from the front of the deque and executed. The action that takes place depends on the type of the value:

  • Words: The corresponding word action is executed.
  • Numbers, Characters: The number or character is printed to stdout.
  • Packs: The pack is unpacked, i.e. all values inside the pack are pushed to the back of the deque.

If the value is marked keep, it is pushed to the end only after its execution finishes.

Words

The following list contains all words. The effect of each word is written in Forth-like stack notation. Conditional operators output either the integer zero or one.

  • dup: x -- x x
  • drop: x --
  • swap: x y -- y x
  • over: x y -- x y x
  • dupd: x y -- x x y
  • swapd: x y z -- y x z
  • nip: x y -- y
  • rotl: x y z -- y z x
  • rotr: x y z -- z x y
  • swapn: x ...[n-1 items] y n -- y ... x (when n is zero, this is no-op)
  • +: x y -- x+y
  • -: x y -- x-y
  • *: x y -- x*y
  • /: x y -- x/y
  • %: x y -- x%y
  • =: x y -- x=y
  • !=: x y -- x!=y
  • <: x y -- x<y
  • >: x y -- x>y
  • <=: x y -- x<=y
  • >=: x y -- x>=y
  • and x y - x&&y
  • or x y -- x||y
  • not x -- !x
  • neg x -- -x
  • keep: x -- .x
  • unkeep: .x -- x
  • toggle-keep: x -- .x ; .x -- x
  • if: cond true false -- true|false (true is outputted when cond ≠ 0, otherwise false is outputted.)
  • pack: x -- [ x ]
  • packn: ...[n items] n -- [ ... ]
  • unpack: [ ... ] - ...
  • input: -- c (reads a single character from stdin)
  • ord: c -- n (converts a character to a number representing its unicode codepoint)
  • chr: n -- c (converts a number representing a unicode codepoint to the corresponding character)

Example Programs

Hello, World!

Prints Hello, World! to stdout.

'H 'e 'l 'l 'o ', ' 'W 'o 'r 'l 'd '! 'u0A

Cat

This program outputs its input.

.input

Truth Machine

Takes a single character as input. When that character is 0, prints 0 and returns immediately. Otherwise, prints 1 forever.

input ord [ 48 ] - [ 0 .1 ] if

Counter

Counts up forever, outputting numbers delimited by space.

.[ dup [ 1 ] + pack [ ' ] ]

[ 0 ]

Limited Counter

Same as the previous, but only counts up to 10.

.[ 
	# increase n
	dup [ 1 ] + pack 
	# compare n to 10 and drop everything if greater
	over [ 10 ] < [ [ drop drop drop drop drop drop drop drop drop drop drop drop drop drop 'u0A ] [] ] if unpack
	# add space to print
	[ ' ]
]

[ 0 ]

FizzBuzz

Does the classic FizzBuzz problem. This program does not halt.

[[ 0 ]]
.[ 
	[1] +
	# select "Fizz" if n%3==0
	dup [3] % [ ['F 'i 'z 'z] [] ] if
	# select "Buzz" if n%5==0
	swap dup [5] % [ ['B 'u 'z 'z] [] ] if

	# push `[drop]` when n%3==0||n%5==0, empty pack otherwise
	swap dup [5] % over [3] % or [ drop [] ] if 
	# create two packs of the number 
	# one is for output, the other is for preserving the state of the counter
	swap pack dup rotl
	# note: above two instructions make sure that the number is only
	# printed when we haven't pushed either Fizz or Buzz

	# print newline (deferred)
	# swap necessary to keep number on top of stack
	[ ['u0A] swap ]
]

Computational class

Sike is Turing complete. This can be shown by a translation argument; the following python code (written by User:Pro465) translates cyclic tag to Sike:

def step(n):
    return ["[[]]", f"[{2*n}]", "swapn"]*2

def convert_bit(bit, n):
    if bit=="0":
        return step(n)
    else:
        return [f"[{2*n}]", "packn", "dup", "unpack",
                f"[{2*n-1}]", "packn", "drop", 
                "unpack", "unpack", "swapn", "unpack"] + step(n)

def convert_block(b, n):
    res=[]
    for bit in b:
        res+=convert_bit(bit, n)
    res+=["[]", f"{len(res)+1}"]
    return f"[[{' '.join(res)}]] drop"

def convert_prog(table, input):
    n=len(table)
    return (" ".join(" ".join(convert_bit(bit, n)) for bit in input)+" "+
            " ".join(convert_block(b, n) for b in table))

# The following two lines can be replaced to generate any cyclic tag system

# Input bitstring
input="100"*1
# Productions
table=["010001","100","100100100","","",""]

print(convert_prog(table,input))

Interpreter

An interpreter for Sike written in C can be obtained here.

Debugging

The official interpreter supports extra debugging features. When run with the -d flag, the interpreter starts in debug mode. When in debug mode, the program can be stepped through cycle-by-cycle by pressing enter. continue or c can be typed to continue execution from the current position without stepping. Breakpoints may also be inserted. For example:

1 2 breakpoint 3

will trigger a breakpoint when 2 is executed. Breakpoints are not words; instead they simply mark the previous token as having a breakpoint. When a breakpoint is triggered, stepping is reactivated.

External resources

Official Sike documentation - Same content as this article.