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 (at least 8 bit) it resets to 0.
  • The interval between counting is undefined. Ideally it approaches 0, because then the interpreter/compiler is optimized.
  • Every scope is treated like a seperate program with "private" timers.

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
'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
- 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).
. Max count any timer can have (implementation defined)
? Value at top of stack. If stack is empty it is ignored.

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.
. Pop top of stack and output as integer.
, Pop top of stack and output as character.
_ Pop top of stack and output as integer followed - even when the stack is empty - by a newline.
" Pop top of stack and output as character followed - even when the stack is empty - by a newline.
# Pop top of stack and insert integer into code. If it doesn't result in a valid command, ignore.
` Pop top of stack and insert character into code. If it doesn't result in a valid command, ignore.
& Get integer from user and push it. (confirmed input, with enter for example)
@ Get character from user and push it. (unconditional input, without enter for example)
+ 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 b>a, otherwise zero.
< Less than: Pop two values a (top) and b, then push 1 if b<a, 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 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|f} If top of stack is not 0 nor is it empty execute code in c, where c are command(s). Else, execute code in f, where f 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 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.
  • 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 underlines (_).

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)

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 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.

({$[]~|&@})		clear stack and request first number and operation
'+'(:^={$$&+_[]~|$})
'-'(:^={$$&-_[]~|$})
'*'(:^={$$&*_[]~|$})
'/'(:^={$$&/_[]~|$})

The code above can be simplified to:

({$[]~|&@})		clear stack and request first number and operation
'+-*/'(:^={$&\`_[]~|$})

The currently smallest known piece of code that acts like a calculator in this language (can be unstable and only one equation per execution):

(&@['`']~)'+-*/'(&^`_~)

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']~)
-(^,~)

Factorial

Takes a user input, calculates and outputs the factorial (repeated user input).

1(^!&^)
?(\:^={$$$.('\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

FizzBuzz{
    3(::^%!{['Fizz']})
    5(\^%!{['Buzz']}+~)
    6-(^,~)
}
1-100(^(FizzBuzz){!"|$^_})

Interpreter

  • If anyone feels the urge to program an interpreter, I suggest not to right now, as some of the specifications are still a bit lacking or incomplete and may still change...
  • Once everything is clear, the creator will try to implement one by himself.

External resources