Timers

From Esolang
Jump to navigation Jump to search

Created by User:Rphii in 2021. Timers is an Esolang that is stack based. In some cases Golfing programs can work out nicely.

Idea

The idea is that Timers relies on time. No need to worry about incrementing anything, because time will do it for you!

Instructions

Time and related

  • At program startup there is one timer whose value is initialized to 0.
  • Every timer counts one up, until a corresponding time function is found, where they will pause until the code inside finished executing.
  • Once a timer overflows it resets to 0.
  • Every scope is treated like a seperate program with "private" timers.
  • Technical note: The interval between counting is undefined. Ideally it approaches t=0, because then the interpreter/compiler is optimized.

Functions and Scopes

Most of the code is executed when called through a function of time.

Command Description
t(c) Function of time, where t gets replaced with a number and c are command(s) See sections Numbers and Commands for a list of possibilities.
s{f} Defines a scope, where s gets replaced with a name and f are functions of time.

Numbers

There are only integers.

Command Description
0-9 0 through 9 can be used to represent numbers. Leading (or single) zeros can be ignored so that 001 is the same as 1
0x.. By using 0x as a prefix, the right side is interpreted as a hex number.
'a' By using ' around one character, it converts that to its corresponding value. Simply use multiple characters to seperate between them, similar to | (below).
| Used for a batch of numbers. Example: 10|20. Any number can be on either side.
- Used for a range of numbers. Example: 'a'-'z' is equal to 'abcdefghijklmnopqrstuvwxyz'. 0-3 is the same as 0|1|2|3 If the left side is not specified its value is defaulted to 0. If the right value is not specified its value is defaulted to the max. count a timer can have (implementation defined). Values on both sides have to be an integer or a batch of numbers with only one character inbetween.
# Used for a linear sequence. Example: 3#2 is equal to 3|5|7|9|11|13|15|17|...[etc]. If the left value is not specified its value is defaulted to 0. If the right value is not specified, it is defaulted to the one on the left. Values on both sides have to be an integer or a batch of numbers with only one character inbetween.
. Max count any timer can have (implementation defined). This also acts like an integer.
? Value at top of stack. This also acts like an integer. If stack is empty and is used on its own or with - or # it gets ignored completely. If stack is empty and it is used with | it only ignores the corresponding side.
! Value one below top of stack. This also acts like an integer. If stack has less than two items and is used on its own or with - or # it gets ignored completely. If stack is empty and it is used with | it only ignores the corresponding side.

Commands

Commands inside a function are executed in order, from left to right.

Command Description
~ Destroy caller's timer after the function finished executing.
^ Push caller's time.
$ Pop top of stack and discard.
\ Swap top stack values.
: Duplicate top stack value.
; Reverse stack. (Deprecated; I think I'll replace it with something else...)
. Pop top of stack and output as integer.
, Pop top of stack and output as character.
" Print a newline.
# Pop top value, n, and push n-th stack value (starting from the bottom). If the stack is smaller than the given index, ignore.
` Pop two values, n (top) and b, overwrite n-th stack value with b (starting from the bottom). If the stack is smaller than the given index, ignore.
? Pop top of stack and insert character into code, if the value is less than 128. Else, insert the value as an integer. If the stack is empty, insert an _ instead. Using multiple of ? will automatically concenate them with whatever is before/after them. The resulting instruction is either one of the command list or a function call. And finally, if it results in an invalid instruction, ignore.
& Get an integer from the user and push it. If a string is entered it will convert the first character to an integer instead.
@ Get a string from the user and push it. (latter characters in the string are pushed first)
+ Addition: Pop two values a and b, then push the result of a+b.
- Subtraction: Pop two values a (top) and b, then push the result of b-a.
* Multiplication: Pop two values a and b, then push the result of a*b.
/ Integer division: Pop two values a (top) and b, then push the result of b/a, rounded down.
% Modulo: Pop two values a (top) and b, then push the remainder of the integer division of b/a.
> Greater than: Pop two values a (top) and b, then push 1 if a>b, otherwise zero.
< Less than: Pop two values a (top) and b, then push 1 if a<b, otherwise zero.
= Equals: Pop two values a and b, then push 1 if a=b, otherwise zero.
! Logical NOT: Pop a value. If the value is zero push 1; otherwise, push zero.
s Execute scope, where s gets replaced with a scope name.
(f) Execute scope, where f gets replaced with function(s) of time. Similar to s above, but inlined.
[t] Initiate a new timer after the function finished executing, starting from t, where t is a number.
{c} If top of stack is not 0 nor is it empty execute code in c, where c are command(s).
{c|d} If top of stack is not 0 nor is it empty execute code in c, where c are command(s). Else, execute code in d, where d are command(s).

Stack

  • At startup the stack is empty.
  • If nothing is mentioned about an empty stack, the following rules apply.
  • When performing any command that pops and uses two values of the stack but the stack only has one value, the missing value is assumed to be 0.
  • Aside from the exception stated above, if the stack is empty, any command that has to do something with it gets ignored.
  • Every scope uses the same stack.

Termination

The program or scope stops as soon as the last timer within that scope was destroyed.

Divide by Zero

Dividing (or modulo) by zero brings some consequences with it:

  • Applies to: Current scope only
  • It destroys all (to the scope known) spacetime, therefore code gets terminated immediately and all timers (within that scope) blend together.
  • Realities are shuffled through and the sole remaining timer takes a random value out of all the possibilities within that scope, except for the max. count any timer can have.
  • TL;DR Randomize. (The non-dramatic version)

Naming Convention

  • Applies to: Scope Names.
  • Has to be unique.
  • Can begin with any of the allowed characters.
  • Can have multiple characters, as long as they're allowed.
  • Allowed characters: A-Z, a-z, 0-9 and underscores (_).

Escape Sequences

  • Applies to: characters (see Numbers section).
  • Timers leans on all the basic escape sequences that are available in C.

Documenting

Text outside of time functions can be used as documentation. (comments) There are however two rules one should keep in mind:

  1. The brackets (){} can't be used as documentation (you can still use []<>)
  2. Since the syntax is pretty forgiving, we need to distinguish between comments and scopes (again, the brackets...). For that, see the scenario below.

Scenario

Suppose you want to end your comment about the function of time 0 with the number 1 (could be any). First, this is how you shouldn't do it:

This is comment nr. 1
(code)

Since it can just as well be interpreted as a comment with a function of time 1:

This is comment nr.
1(code)

This can be handy in some situations, but not in this one, so here's two workarounds:

This is comment nr. 1

(code)

Notice how there is an extra line between the comment and the function of time 0. This indicates that those two items (comment and code) are indeed seperated.

This is comment nr. 1
0(code)

In the example above we explicitly said that the function is a function of time 0.

Rules

Flow of Execution

  • The program starts executing in the main scope.
  • The main scope is any code outside of defined scopes.
  • Only the timers of the current scope are able to count.
  • Once a scope is terminated, flow of execution is returned to its parent scope.

Timers

  • Timers can be explained with a virtual stack model.
  • All timers in the active scope count when there is no matching time function.
  • Batches of timers get pushed in reversed order. Eg: [0|1] => pushing 1, then 0
  • Upon creation of timers, they get pushed to a virtual stack. Eg: [0][1] => pushing 0, then 1
  • Newly pushed timers (top) take priority over older timers.
  • A timer calls each unique time function at most once, when counting is halted.
  • Time functions are searched and executed forwards from the end of the calling function and wrap around back to the beginning (itself).

Assume we have the simple example below. (WXYZ are line numbers used to explain)

V: 1([2])
W: 1(~) 
X: 1(...)  this one never gets called
Y: 0([2|1])
Z: 2(...)  ... should represent some code inside
  1. Initial timer A has the value 0. We search for a time function and the first one we find is on line Y.
  2. The function on line Y gets called once. The function creates two new timers: B starting from 1 and C starting from 2.
  3. Timer C takes priority over timer B, because C is newer. (priorities, values: C=2 > B=1 > A=0)
  4. As soon as the function finished executing, we search for matching time function or increment the timer's values.
  5. We find a matching time function for timer C on line Z. Its function gets executed once.
  6. Next up we find a matching time function for timer B on line V. It creates a new timer D who starts at 2 and exits the function. (priorities, values: D=2 > C=2 > B=1 > A=0)
  7. Timer D has one matching time function on line Z, which gets executed once.
  8. After that, timer B has another matching time function on line W. Its function gets executed once. After executing the code inside it, it destroys that timer. (priorities, values: D=2 > C=2 > A=0)
  9. Now there is no remaining and matching time function, increment all timers (by one). (priorities, values: D=3 > C=3 > A=1)
  10. Find a matching time function, starting from the highest priority.
  11. Timer D and C have no matching time functions, skip.
  12. Timer A has a matching time function on line V. Execute it once: Create a new timer D, starting from 2. (priorities, values: D=2 > C=3 > B=2 > A=1)
  13. Timer D has a matching time function on line Z. Execute it once.
  14. Timer D now has no matching time functions.
  15. Timer A has another matching time function on line W. After executing the code inside, timer A gets destroyed. (priorities, values: D=2 > C=3 > B=2)
  16. Now, there is no remaining and matching time function, increment all timers (by one, until any timer (in this example timer D) hits a time function (in this example line Y)
  17. The whole process begins again
  18. This example would not result in a good output, because we exponentially create timers.

TL;DR: Be careful.

Examples

Truth machine

(&!{!.~})
1(^.[1]~)

Cat

?(,[?]~)(@)

Calculator

Takes a value, an operation (+, -, * or /), a second value and outputs the result.

(&&&\?."~)

Hello World

(['Hello, World!\n']~)
'H'(^,~)
'e'(^,~)
'l'(^,~)
'o'(^,~)
','(^,~)
' '(^,~)
'W'(^,~)
'r'(^,~)
'd'(^,~)
'!'(^,~)
'\n'(^,~)

Is essentially the same as:

(['Hello, World!\n']~)
'Helo, Wrd!\n'(^,~)

And it can be simplified to:

(['Hello, World!\n']~)
-(^,~)

Fibonacci

Prints the Fibonacci sequence in order.

1(^^!(?(:."+^\)))

ASCII Table

This program prints the ascii table. From ASCII with values 33 to 126.

33-126(^,).("~)

FizzBuzz

f{(['Fizz']~)-(^,~)}
b{(['Buzz']~)-(^,~)}
1-100(^(3(:^%!{f}~))\(5(:^%!{b}~))\$+!{^.}"$)
.(~)

Interpreter

  • It is being worked on with steady progress. Changes to the wiki will still happen though.

External resources