Marz
Marz is a 2D, self-modifying, object-oriented esoteric programming language in development by a madman User:SoundOfScripting.
This specification may or may not be representative of the future specification and is subject to change without discretion.
Wait, what?
Yes, you read that right.
Programming in Marz
Marz programs are one of the following:
1) A single program file.
2) A collection of files including an index file. The index files lists the order that the program files are loaded/executed in. The format for index files is not decided.
Marz program files should have the file extension .mz
.
Scope
Marz uses lexical scope for variables. A "scope"/grid is a two-dimensional grid of characters which holds variables or code.
Variables of a scope are parsed at runtime when accessed.
Each program file in Marz is its own scope. There is also a global scope, which holds each of the program file scopes in variables.
An in-place/one-dimensional scope is a scope that is not a two-dimensional grid of characters. These scopes are not self-modifying and cannot be created by the program. The global scope is in-place.
Parser and IP
For each program file, the starting position of the scope is initialized to the top left corner. The starting position marks where parsing and execution begin.
Each two-dimensional scope has its own parser, which moves over the characters to store and retrieve variables. Every time a variable is accessed by name from a scope, the parser moves through the scope until it finds the variable and returns it (if it is not found the program gets stuck in an infinite loop). If a syntax error is parsed, parsing stops.
The IP (instruction pointer) is similar to the parser. There is only one instruction pointer for the whole program. The instruction pointer moves through scopes executing code, not parsing for variables.
Note: A scope's parser only begins parsing when one of its variables are accessed.
Control flow
If the IP or parser moves off of the edge of a scope, it loops around to the opposite edge.
Directional arrows (←
, ↑
, →
, ↓
) change the direction of the IP and parser.
↓ Starting point: top left ← wraps → Inf ↓ wraps ↑ loop ←
Note: the above code snippet is not valid syntax. It is just an example of control flow.
Referencing variables
Variable names may consist of any characters.
Variables with names only consisting of alpha-numeric characters (and with a letter as the first character) may be refered to simply by writing the variables name.
Scope addressing
Placing an addressing prefix before a variable name will access that variable in a different scope.
Without an addressing prefix, the current scope is accessed.
Prefix | Description |
---|---|
# | Refers to the current scope. |
$ | Refers to the global scope. |
_ | Refers to the scope containing this one (the scope above it). Stackable. |
To refer to a variable with a non-alphanumeric name, you must use an addressing prefix followed by the name in quotation marks.
Examples:
$"_abacaba()[]]"
|
Refers to the variable named _abacaba()[]] of the global scope.
|
foobar
|
Refers to the variable named foobar of the current/local scope (the scope this line is executed in).
|
__str
|
Refers to the variable named str two scopes above this one.
|
#"fizz buzz"
|
Refers to the variable named fizz buzz of the current scope.
|
Global scope
Variable on global scope | Description |
---|---|
class Number
|
Literal class for Numbers. |
class String
|
Literal class for Strings. |
class Void
|
Literal class for Voids. |
Void print(String str)
|
Writes str to the output.
|
Void println(String str)
|
Writes str to the output followed by a newline.
|
String inputLine()
|
Returns the next line of input (might be changed/removed). |
String inputChar()
|
Returns the next character of input (might be changed/removed). |
Variables
Variables in Marz can store literal data, scopes, and classes and instances (both contain their own scopes).
Data types
Marz has four main data types: String, Number, Boolean, and Void (null). These data types are classes stored on the global scope and are not instantiable.
There are also three sub-types --- Array []
, Function ()
and Reference :
--- which may be appended to the end of a type to create a new one.
Compound types, written as (Type1, Type2, ...)
, denote a value of any of the types compounded. For example, (String[], Number)
may store an array of strings or a number.
Voids
A Void in Marz stores no data. Typically, when a value is not written, a Void is supplied.
Booleans
Booleans in Marz can store two values, true
and false
.
//TODO: write Boolean section
Numbers
Numbers in Marz are all instances of the base number class, $Number
.
Numbers are stored as computable real numbers ?with infinite precision?.
Numbers may be written in any base according to the following prefix table.
Prefix | Base | Example |
---|---|---|
None | Decimal | 123 = 123
|
0x
|
Hexadecimal | 0x7B = 123
|
0c
|
0c173 = 123
|
Octal |
0b
|
Binary | 0b1111011 = 123
|
0(X) , where X is a number (nestable)
|
Base X | 0(0(7)12)146 = 0(9)146 = 123
|
Prefixing a number with a -
denotes a negative value. Any whitespace read while parsing a number is ignored.
Numbers may not start with .
, it must be prefixed by a 0 (ex: 0x.8A9
is invalid, but 0c0.417
is).
Note: Although 0(10)64
and 64
are equivalent, they are still stored differently.
Number operators
Numeric operations take two numbers as arguments and return a result written in the same base and representation as the first.
Numbers are never rounded and may use "infinite" decimal places (see infinite numbers and strings and stuff when I write the things and stuff about them).
Numeric operations are evaluated using the order of operations and ()
to indicate evaluation order.
Operator | Description | Example |
---|---|---|
N1 + N2
|
The sum of N1 and N2 | 13 + 0x1F ==> 44
|
N1 - N2
|
The difference of N1 and N2 | 0(9)65 - 0c15 ==> 46
|
N1 * N2
|
The product of N1 and N2 | 0(10)6 * 4 ==> 0(10)24
|
N1 / N2
|
The quotient of N1 and N2 | 0c1 / 0(8)3 ==> 0c0.252525...
|
N1 ** N2
|
N1 to the power of N2 | 2 ** 3 ==> 8 1 ** 2 ** 3 is read as 1 ** (2 ** 3)
|
NaN
, Infinity
, and -Infinity
NaN
is a special number returned when a numeric operations has no valid value. Infinity
reciprocal of 0
, and -Infinity
is its opposite.
Similar to their floating-point equivalents, NaN
swallows all numeric operations, and +/-Infinity
do the same in some scenarios.
On the global scope, $NaN
holds NaN
, and $Infinity
holds Infinity
. However, the values may still be computed as follows:
→123← 1232123212... -> Infinity →0← 0000000000... -> NaN →123.→123→← 123.123123... -> Not infinite. →1/0→← Infinity →-1/0→← -Infinity →1 / $Infinity→← 0 →1/-0→← Infinity (there is no negative 0, so -0 = 0) →-1 ** 0.5→← NaN (only real numbers are supported) →0/0→← NaN →0 * $Infinity→← NaN →2 ** $Infinity→← Infinity →2 ** -$Infinity→← 0 →1 ** $Infinity→← NaN Definitely not stolen from JavaScript
Number class
The Number class, $Number
may be instantiated as follows:
→Number( (Number, Boolean, Void)num
)← Convertsnum
to decimal (no prefix) Void -> 0, true -> 1, false -> 0 Equivalent to casting the value (($Number) true
==> 1) →Number( (Number, Boolean, Void)num
, Numberbase
)← Convertsnum
to basebase
(prefix 0(base
)) →Number( Stringstr
)← Parsesstr
, returns a number in the base/representation ofstr
Also equivalent to casting the value. Ex: Number("0xF3B0") = 0xF3B0 Number("0(8)11") = 0(8)11 Number("72") = 72 Number("abc") = NaN →Number( Stringstr
, Numberbase
)← Parsesstr
, returns a number in basebase
→Number( Stringstr
, Numberbase1
, Numberbase2
)← Parsesstr
as a basebase1
number, returns a number in basebase2
There are also the following properties of the class:
Property | Description | Example |
---|---|---|
static Boolean $Number.valuesEqual( Number a, Number b ) | Compares two numbers' values (not bases). Equivalent to the comparison operator == .
|
$Number.equal(12, 0xC) ==> true
|
static Boolean $Number.basesEqual( Number a, Number b ) | Compares two numbers' bases (not values). | $Number.basesEqual(0(10)75, 43) ==> true
|
static Boolean $Number.equal( Number a, Number b ) | Returns whether two numbers are the exact same value, base, and representation. | $Number.equal(75, 75) ==> true |
Comparing numbers
Marz's comparison operator is ==
. When applied to numbers, ==
will return whether the Numbers' values are equal (not their bases). To compare values and bases, use static $Number.basesEqual( Number a, Number b )
.
Strings
Strings are a sequence of characters surrounded by ""
.
Examples:
→← ; " f →"abcdef";→← →↑ →"abc↑
Note: Literal Strings and Number do NOT wrap around edges, only the IP/parser do.
//TODO: finish this
Declaration statement
Finally, what you're probably been waiting for. How does one create a variable?
→$String str = "Hello, World!";→← As expected.
The above is a declaration statement. The statement itself stores the value of the variable.
Changing a variable's value will write into the declaration statement (if the parser can find it, if not infinite loop).
The parser will only find the first declaration statement of a variable, then exits. So, any duplicate statements are ignored, and redefining a variable sets its value.
Executing this:
→$String part1 = "Hello,";↓ ↓ ← →$String part2 = "World!";↓ ↓ ← →part1 = part1 + " " + part2;→← Assignment statement.
Results in the following grid:
→$String part1 = "Hello, World!"; ↓ ← →$String part2 = "World!";↓ ↓ ← →part1 = part1 + " " + part2;→← Assignment statement.
As you can see, writing to part1
overwrote the ↓
. This leaves the parser stuck looping over line 1 (due to wrapping), so it can no longer see the declaration statement for part2
(meaning that variable no longer exists).
The IP, however, is not affected by this overwriting. It still continues on line 5 and gets stuck in its own infinite loop.
Auto-evaluation of declaration statements
When a declaration statement is executed, it evaluates it and writes the result (like an assignment statement).
For example:
→$Number x = getX();↓ ↓ ← →$Number getX(){↓ The ()/[]/: may be placed after the variable name instead of the type, which does the same thing. ↓ ← → return 12345678;↓ ↓ ← →}
When executing line 1 of the above, the IP stops at the ;
. Then, the expression getX()
is evaluated, returning 12345678
, which is written to x
.
This results in the ↓
being overwritten with an 8
. The IP and parser get stuck on line 1, repeatedly rewriting 12345678
to x
.
Note: When writing a value to a variable, a ;
is placed at the end. Written values start at the first character of the previous value in the declaration statement. No extra whitespace is written.
Storing an unbounded value
Here is an easy method of storing an unbounded value (using wrapping).
→↓ ↓→$String str = "aaaaaaa"; →str += "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";→← Will never cause any problems.
Of course, this is assuming that str
never contains any directional arrows, which still work when written in Strings (see: Escape characters, once I write about them).
Variables in scopes
To refer to a variable in a scope, use a .
.
On the global scope, for each program file, a variable with a name consisting of the file name and relative path to the index file of that program file stores that program file's scope.
For example:
//index file format TBD, order is "A/B.mz", "C.mz" //A/B.mz →$Number x = 5; //C.mz →$Number y = $"A/B.mz".x + 2;→←
This results in the following grid for C.mz
:
//C.mz →$Number y = 7;A/B.mz".x + 2;→←
which causes a syntax error for the parser (exiting parsing at that point (the "
)).