Numbers

From Esolang
Jump to navigation Jump to search
Numbers
Paradigm(s) imperative
Designed by User:Xorol
Appeared in Category:2021
Memory system Two stacks
Computational class Turing complete
Major implementations [1]
File extension(s) .nums, .nmod

Numbers is an esoteric programming language created by User:Xorol. The main gimmick is that all of the commands (with some exceptions) are numbers. It uses two stacks, but only one can be used at a time.

Data storage

Numbers stores its data in two stacks: the main stack and the control stack. Most commands that perform operations on the stack are indifferent as to which stack they're using. But, most of the commands starting with 4 will only use the control stack.

Toggling stacks

You can toggle between stacks with the command 21. The program starts with the main stack selected.

Commands

Commands are split into four groups: arithmetic, stack manipulation, IO, and control flow. Arithmetic commands start with 1, stack commands with 2, etc.

Command Arguments Description
Arithmetic
10 2 Adds a + b
11 2 Subtracts a - b
12 2 Multiplies a * b
13 2 Divides a / b
14 2 Floor divides a // b
15 2 Modulos a % b
16 1 Increments a++
17 1 Decrements a--
18 1 1 if a is negative, else 0
19 1 Factorial a!
Stack manipulation
20 Syntactic Pushes the number after it to the stack. Alias: *
21 0 Toggles which stack is selected
22 2 Swaps a and b
23 1 Pops a
24 1 Moves a to the unselected stack
25 1 Moves a (from the unselected stack) to the selected stack
26 1 Duplicates a
27 0 Clears the stack
IO
30 1 Prints a as it is
31 1 Prints a as a character
32 entire stack Prints the entire stack as integers, separated by spaces
33 entire stack Prints the entire stack as a string
34 0 Gets input as an integer
35 0 Gets input as a character, pushing its Unicode representation
36 0 Gets input as a string, pushing each character's Unicode representation
Control flow
40 1 Only executes the next command if a is not 0
41 1 Only executes the next command if a is 0
42 1 Sets the pointer to a
44 1 Defines a function
45 1, and syntactic args Maps a as a function onto all following numbers
46 entire stack Loads the file specified by the stack as a module

There's also a special command: ~ which ends the program.

Further explanation

20

Pushes the number after it to the stack. Examples:

20 5
*5

Both of these push 5 to the stack and are interchangeable.

40 & 41

These will only execute the command after them if the top value on the stack is (or, in the case of 40 is not) 0. Examples:

This will print 1

*1 40 30

This won't do anything

*0 40 30

But this will

*0 41 30
44

Defines a function, with the following syntax:

*function_name 44 mapping_compatibility function_code 44

Replacing function_name with the name of the function, mapping_compatibility with either a 0 or 1 depending on whether the function is to be compatible with mapping (45), and function_code with the code for the function. For example:

*6 44 0 *6 30 44

Defines a function 6, that is incompatible with mapping, and that prints a 6 whenever run.

45

Maps the top value on the stack as a function onto all of its arguments. For example:

*16 45 1 2 3 4 5 6 7 8 9 45

Maps 16 (increment) onto 1 2 3 4 5 6 7 8 9 and puts the results on the stack, in this case 2 3 4 5 6 7 8 9 10.

Built-in functions

Numbers has a number of built-in functions. All built-in functions are accessible using 10.x, replacing x with the name of the function.

Command Arguments Description
10 2 1 if a == b else 0
11 2 1 if a > b else 0
12 2 1 if a < b else 0

Comments

Comments in Numbers are done using a ;. For example:

; This is a comment
*10 30 ; this is a comment
*10 30 ;this is a comment
*10 30; error: 30; is not a command

Block comments are done with a double semicolon (;;) at the start of the opening and closing lines:

;; Start of a block comment
inside 
still inside
;; End of the block comment

Meta-comments

Meta-comments start with ;! and must be on their own line. They are special comments and affect how the program runs. Here are the ones that are required for a Numbers implementation:

Comment Arguments Description
NOBUILTINS 0 Disables built-in functions
USE 1 Used for modules. Defines the namespace into which the module's contents should be loaded

The main implementation linked at the top also implements these meta-comments:

Comment Description
NOILC Disables the interpreter's infinite loop checker
DEBUG Activates debug mode
TOOLS Opens the tool menu instead of running the program.

These are explained in full in the interpreter's README

Stack references

Stack references act as a way to use values that are not on top of the stack. They use the syntax $x, replacing x with the index from the top that is being referenced. These can be used as syntactic arguments only; if used as stand-alone commands, an error will be thrown. Examples:

*$1 ; push the second item on the stack to the top
44 $3 ... 44 ; using the fourth item as mapping compatibility
 
$2 ; Error: stack references cannot be used as commands.

Namespaces

Namespaces are containers for functions. All functions within a namespace can be accessed using x.y, replacing x with the namespace's name and y with the function's name. Namespaces can also contain other namespaces, nesting infinitely. They can also be empty. To define one, use the following syntax:

15 {
   1 : 0 : *1 30
   2 : {}
}

This defines a namespace 15 with two things inside it: a function 1 (accessible as 15.1) and an empty namespace 2 (accessible as 15.2). Note how the syntax is different inside namespaces: functions use colons instead of the normal 44 syntax, and namespaces have a colon before the opening brace.

Notes

  • Make sure you don't have a function spread across two or more lines; functions may only occupy a single line.
  • Comments must be on separate lines.

Modules

Modules are just namespaces, but loaded in from a different file, all module files have the extension .nmod. For example, the code from earlier as a module would look like this:

;!USE 15
1 : 0 : *1 30
2 : {}

That ;!USE 15 defines the namespace the module's contents should be loaded as, besides that, the syntax is exactly the same as for namespaces. Once the module is loaded, the functions inside are accessible as if it were a normal namespace.

To load a module, you have to get its filepath first. The filepaths are all relative to the location of the script. Here's an example filesystem with the corresponding filepaths beside them:

root
 |-a.nmod (^^a)
 |--utils
 |    |-b.mod (^b)
 |    |--other
 |        |-c.nmod (c)
 |        |-script.nums
 |--math
    |-d.nmod (^^math/d)
    |--complex
          |-e.nmod (^^math/complex/e)

As you can see, / is used to go down a level and ^ is for going up a level. To load it, push the corresponding unicode representation to the stack and run 46.

Example programs

Hello, world!

The Hello World! program pushes each letter's corresponding ASCII values onto the stack, then prints the stack as a string.

*20 45 72 101 108 108 111 32 87 111 114 108 100 33 45 33

Truth-machine

The truth-machine takes input from the user, if it is a "1" it outputs "1"s indefinitely, if it's a "0" it outputs a singular "0"

34 26 26 30 41 ~ *2 42

Cat program

A cat program outputs whatever the user inputs.

36 33

An infinite version:

36 33 *0 42