Not Python
Not Python is an esoteric programming language created by User:PythonshellDebugwindow.
Syntax
Programs in Not Python take the form of a series of top-level constructs. There are four types of top-level constructs: imports, variable definitions, constant definitions, and function definitions. All top-level constructs in a program, with the exception of imports, are sequentially assigned IDs starting at zero; the first construct has the ID 0, the second has the ID 1, and so on.
Imports
Functions from the standard library can be imported using the syntax [vImport ^name]
, as can syntactic extensions. For example, the following statement would import the function MATHPI
:
[vImport ^MATHPI]
Variable and constant definitions
Variables can be declared using the syntax [_name_value]
. Similarly, constants can be declared using the syntax [_name__value]
. Names must be alphanumeric; the first letter must be capitalised, and all other letters must be lowercase. Values must be integers, addition expressions, calls to previously imported functions, or some combination of these. For example, the following statements would define the constant Tau
and the variable Foo
:
[vImport ^MATHPI][_Tau__$MATHPI+$MATHPI][_Foo_123]
Variables and constants are available at any point in a program after their definition. Names cannot be referenced elsewhere in a program; they are only useful as documentation.
Function definitions
Functions can be defined using a pair of curly braces ({}
):
{function body}
Statements within functions can be separated by semicolons (;
). Empty statements are invalid, which means that a semicolon cannot be the first or last character in a function body.
The final function defined in a program is the main function, which serves as the program's entry point.
With the exception of calling imported functions, the rest of the syntactic elements described in this section are only valid within function definitions.
Returning values
For a value to be returned, it must be preceded by an asterisk (*
). For example, the following function would return the number 123:
{*123}
Each user-defined function must contain exactly one return statement. The return value of the main function is interpreted as the program's exit code: zero indicates failure, while all other values indicate success.
Arguments
A function can access its Nth argument using the syntax @@N
. Argument indexing starts at 1. For example, the following function would return the sum of its first three arguments:
{*@@1+@@2+@@3}
If an undefined argument is accessed, it will evaluate to nil. This is the only way in which nil can be obtained by a program. If a program attempts to output nil or use it as an argument to +
, multApp
, read
, or find
, execution of the program will be restarted.
Calling functions
User-defined functions can be called using a dollar sign. For example, in the following program, the main function would call the function with the ID 0
and return successfully:
{*1}{*$0}
Functions can be called dynamically. For example, the following function would call a different function depending on its first argument:
{*$@@1}
Imported functions can be called in much the same way, although they cannot be called dynamically. Assuming that MATHPI
has already been imported, the following function would call it and return its return value:
{*$MATHPI}
If the program attempts to call the main function or something which is not a function, or specifies an invalid or nonexistent ID, execution will be restarted. Functions additionally cannot call functions which are defined later in the program, as attempting to do so will also cause execution to be restarted. For example, in the following program, the first function ($0
) attempts to call the second function ($1
), but instead restarts execution, thus creating an infinite loop:
{*$1}{*0}{$0;*1}
Function arguments can be specified using the percent sign %
or the permille sign ‰
; the two signs are treated identically. The following function would call the function with the ID 0
, passing it the number 5, and return its return value:
{*$0%5}
If a function is called as an argument to another function, it will not be passed any arguments. For example, in the statement $0%$1%456%789
, assuming that $0
and $1
are both functions, $0
would be called with the arguments $1
, 456
, and 789
, while $1
would not be passed any arguments.
Accessing and modifying variables
Variables can be accessed and modified using the same syntax used to call a function. If at least one value is provided using a percent or permille sign, then the variable will be set to the first provided value; otherwise, it will be accessed. For example, the following statement would set the variable with the ID 0
to 123
:
$0%123
The specified ID must be an integer literal. It is a syntax error to attempt to modify a variable within an expression; for example, the expression 1+$0%123
would be syntactically invalid if $0
refers to a variable. Attempting to modify a constant is also a syntax error. The ID of a variable to be accessed or modified cannot be determined dynamically; doing so would cause the expression to be interpreted as a call to a function. If more than one value is given, all but the first will be ignored, although all of them will still be evaluated; for example, the statement $0%1%2%3%4
would be equivalent to $0%1
if $0
refers to a variable.
Output
If text is preceded by <
and followed by > printer:
, it will be output, followed by a newline. Angle brackets can be nested within such statements; programs with unmatched angle brackets are syntactically invalid. As an example, the following program would output Hello, <world>!
followed by a newline:
{<Hello, <world>!> printer:;*1}
Other values can be printed by simply appending printer:
(note the leading space). The following statement would output the number 123 (without a newline):
123 printer:
If a fixed-point number is output, exactly ten decimal digits will be printed to the right of its decimal point.
The shorthand notation a,b printer:
is equivalent to a printer:;b printer:
; there is no limit to the amount of values which can be printed using this notation.
The most recently output value can be retrieved from tray
. Note that tray
remembers the type of the most recently output value. For example, the following program would output 123128
:
{123 printer:;tray+5 printer:;*1}
If a function attempts to access tray
when no value has yet been output, the function will immediately return the integer 0. For example, the following program would not output anything, but instead exit with code zero:
{tray,< world!> printer:;*1}{*$0}
User input
User input can be read in two ways. The word read
will read user input until the user enters the next character in the source code; for example, the expression read}
would evaluate to the string {ABCD
if the user input the characters {ABCD}
. An N-digit integer can be read using find N
; characters will be read one by one until N digits have been entered. read
evaluates to a string, while find
evaluates to an integer.
Multiple application
Functions can be applied multiple times using the multApp
operator. Its syntax is F%X multApp N
. Given a function F, an initial value X, and an application count N, it will run the following algorithm:
- If N is negative, stop and evaluate to X. Otherwise, go to step 2.
- Calculate the result of F(X).
- Set X to the calculated result.
- Decrement N. If it is less than or equal to zero, stop and evaluate to X. Otherwise, go back to step 2.
As an example, in the following code snippet, the second function invokes the multApp
operator. F is equal to $0
, X is 2
, and N is 3
.
{*@@1+5}{*$0%2 multApp 3}
The operator would evaluate to 17 (2 + 5 + 5 + 5).
Note that X and N are evaluated, in that order, before the application algorithm is run.
Forever operator
An infinite loop can be created using the Forever operator, which consists of the prefix Forever =
(note the leading space) and the suffix /Forever
. Neither component of the operator can be preceded or followed by a semicolon. For example, the following program would print the digit 0
followed by infinite 1
s:
{0 printer: Forever =*1 printer:/Forever*1}
The Forever operator can be used in expressions, although it will not evaluate to any result, as it will only terminate iff the entire program terminates.
Values
Not Python supports three datatypes: numbers, strings, and nil.
Numbers, which are unbounded, can either be integers or fixed-point numbers; the latter store ten fractional digits.
Integers are represented in programs as sequences of the digits 0-9. Fixed-point numbers can only be obtained by importing MATHE
, MATHG
, or MATHPI
from the standard library. Strings can be obtained in two ways: they can be retrieved from tray
or read using read
. Nil is described in the #Arguments section.
Addition operator
The +
(addition) operator can be used for addition, stringification, or random number generation depending on the types of its operands.
If both of its operands are numbers, the operator will evaluate to their sum. For example, the following program would print 10
:
{1+2+3+4 printer:;*1}
If its left-hand operand is a string, it will convert its right-hand operator to a string and evaluate to the result. For example, if tray
contains a string, the expression tray+123
will evaluate to the string 123
. If a fixed-point number is converted to a string, there will be exactly ten decimal digits to the right of its decimal point.
If its left-hand operator is a number and its right-hand operator is a string, the operator will evaluate to a random integer between 1
and the current output radix (see the RAD
function in the #Standard library), inclusive at both ends.
Syntactic extensions
Syntactic extensions can be imported using the same syntax as is used to import library functions. For example, the following statement would make the COMPARISON
extension available to the program:
[vImport ^COMPARISON]
If an extension is imported, it is available everywhere in the program. Like normal imports from the standard library, imports of extensions are not assigned IDs. If an extension is imported more than once in a program, the program is syntactically invalid.
The only syntactic extension available to programs is the COMPARISON
extension.
Comparison
The COMPARISON
extension allows programs to use the <
(inferior), >
(superior), ><
(inequality), and <>
(rhombus) operators. The first three evaluate to 1 if their left-hand operand is respectively less than, greater than, or not equal to their right-hand operand, and otherwise to 0. The rhombus operator evaluates to -1, 0, or 1 if its left-hand operand is respectively less than, equal to, or greater than its right-hand operand. If one of these operators is given a non-number operand, it will evaluate to 0. Note that <
and >
must still be matched within a program.
As an example, the following program would input two digits and check whether they are equal:
[vImport ^COMPARISON]{*0<1}{find 1><find 1><1;*1>0}
It first compares the two digits using the inequality operator, and then compares the result to the number 1. The inferior and superior operators have been used so that each <
has a matching >
and vice versa; note that these matches can occur across functions.
Standard library
The standard library consists of the following functions.
Function | Effect |
---|---|
DIST |
Returns the Levenshtein distance of its first two arguments if they are both strings, and causes the program to terminate successfully otherwise. |
EXIT |
Causes the program to terminate successfully. |
MATHE |
Returns the constant 2.7182818284. |
MATHG |
Returns the constant 1.6180339887. |
MATHPI |
Returns the constant 3.1415926535. |
NIL |
Returns 1 if its first argument is nil, and 0 otherwise. |
RAD |
If its first argument is an integer N between 2 and 62 inclusive, integers will be output in base N (where the digits of base 62 are 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz in that order) until such time as the RAD function is called again; if its first argument is not valid, the function has no side effects. Fixed-point numbers and strings are not affected. Returns the base in which integers would be output just before this function was called.
|
If a program enters an infinite loop using the Forever operator, the DIST
and EXIT
functions will no longer have their original effects; they will instead return the integer 1 and produce no side effects. They will only be re-enabled if the execution of the program is restarted.
Operator precedence
The following table shows the precedence of operators in Not Python. Operators with higher precedence are evaluated first, and operators with equal precedence are evaluated from left to right.
Precedence | Operators |
---|---|
7 | Forever operator |
6 | @@
|
5 | multApp
|
4 | +
|
3 | find
|
2 | < , > , >< , <>
|
1 | $ , %
|
Examples
Untested.
Hello, world!
{<Hello, world!> printer:;*1}
Cat
{ Forever=read printer:/Forever*1}{*$0}
Alternatively:
{read printer:;*$0}{*$0}
Truth-machine
{0 printer:;*1}{1 printer:;*$1}{*$find 1}
99 bottles of beer
[vImport ^COMPARISON][_Bottles_99][_Negativeone_0]{$0,< bottles of beer on the wall,>,$0,< bottles of beer.>,<Take one down, pass it around,> printer:;$0%$0+$1;$0,< bottles of beer on the wall. > printer:;*0}{$1%0<>1;$2%0 multApp 97;<2 bottles of beer on the wall,>,<2 bottles of beer.>,<Take one down, pass it around,>,<1 bottle of beer on the wall. >,<1 bottle of beer on the wall,><1 bottle of beer.><Take one down, pass it around,>,<no bottles of beer on the wall.> printer:;*0}
Add inputs
{<Enter number (000-999):> printer:;*@@1+find 3}{<Enter number of numbers (000-999):> printer:;$2%0 multApp find 3,< is the sum.> printer:;*0}
Circle area calculator
[vImport ^MATHPI]{*@@1+MATHPI}{*$0%0 multApp @@1}{*$1%0 multApp @@1}{<Enter radius (000-999):> printer:;$2%find 3,< is the area.> printer:;*1}