Mylange Programming Language
"Mylange; it rhymes with orange."
Welcome to the Mylange Programming Language!
This language is developed by Myriware as a simple yet powerful
language that can be used by all, especially here at Myriware.
So, what is it? Mylange is both a functional and object oriented programming language.
It takes many elements from different languages and weaves them together.
Mylange is a strict-typed language.
The Files
The most important part of Mylange is storing it in files, which are read by
the Mylange File Interpreter. Alternitivly, for developement uses, the
Mylange Linear Interpreter is used, much like Python's CLI Interface.
All mylange files are defined by the .myl extention.
The interpreter can be installed on the downloads page.
Mylange features a Rust-like garbage collection system. All variables or parameters are assigned
to a scope block. When the block ends, the values inside the scope are all removed, and the memory
is free.
Mylange Interpreter works on an extensive caching system, which allows for easier interpretation
of string, chars, and bracket blocks. They are, however, stored together in one Blockings Map,
and there is only one counter for the combined efforts. Each string, char, or block gets replaced
by a hex code, looking like 0x00000000. Due to the structure, please refrain from
using more than 4.294967296*10^9 strings, chars, and bracket blocks, as it will most likely break the system.
The docs are being rapidly updated as fast as we can. Please give some time for them to be fully updated.
Directives
Comments
Comments are the simplest part of the language, which are
useful for documentation and developement. They will be used through this
guide to help out. Single line comments are defined by double-slashes (//).
Multiline comments are defined with slash-bracket pairs, /[...]/.
// This is a single line,
// it cannot span mutliple lines
/[
The multi-line comment, also called block comment,
can span multiple lines!
]/
Include
The first thing that you should learn is how to get access to builtin functions.
This includes any function that Mylange provides. All functions belong to a package,
with an ID that is normally short and an abreviation of something.
In order to use the package, you need to include it, denoted by:
#include <PACKAGE_ID>;
#include "FILE_NAME" as <PACKAGE_ID>;
For example, the io package provides ways to input and output into the
terminal, such as the famous print function. After the package is
included, the function can be called in the default function calling way. The
function lives under the package, so a prefix of the package ID and a dot is required.
For example, to print "Hello world", the following code is used:
#include <io>;
io.print("Hello world!");
You can also access your local files using the #include directive.
For local files, use double-qoutes instead of angled brackets, and reference the file name (without .myl extension).
Optionally, you can also speicify the as keyword, followed by a name in angled brackets,
to change the name of the local file you are including. If you ommit the as name, then
the name of the module is just the name of the file.
For example:
#include "example" as <ex>; // includes the local file "example.myl" as "ex"
ex.main(); // run all functions defined in the module like a builtin module.
Continue
#continue "FILE_NAME";
Another important directive is the continue directive. This basically allows you to jump out of one file,
and into another to continue execution. It will come back after the statement is done.
This is useful because it will allow for the other file to run in the current scope, meaning that
the file is running as if you wrote it where the directive is.
//ex.myl
str msg => "Hello world!";
io.print(msg);
//main.myl
#include <io>;
io.print("Running from the main file");
#continue "ex";
io.print(msg);
This directive is designed to make code files shorter, as well as add clearity to files.
Types
The beginnings of this language are the types that data can be stored as.
The following are Primitive Types, which are hardcoded into the language,
and serve as the building blocks for all other types and classes.
nil |
an empty value, like null. |
nil |
boolean/bool |
true or false. |
true or false |
integer/int |
Any integer number. |
12, -12 |
character/char |
A single character. |
'a', 'b' |
string/str |
A combination of different characters. |
"string" |
array/arr |
A list of items. |
[1, 2, "three"] |
set |
An object made of key/value pairs. |
(key=>"value", some=>"thing") |
type |
Represents a type in the language |
$int, $array<char> |
Aside from these are special types, like casting, this,
dynamic, and union.
These are not types that users will call, they are more code orientated.
Castings are objects made from user-defined classes. This is used within class castings
and are always linked to the keyword this.
Dynamic is used for loops, like when a list has mixed types inside.
Unions are not implemented.
Some types can be artitectured, meaning, you can define what the elements inside them
represent. For example, arrays and sets ordinarily can have any type inside them.
However, if you artitecture them, then they can only have certain elements inside them.
The way to do this is to follow the type name by the architype inside angle
brackets (<>). So, an array of only chars can be represented as
array<char> letters => ['a', 'b', 'c'];
array<array<int>> co_ords => [ [1, 2], [3, 4], [5, 6] ];
Variables
Storing variables is a basic task, but one that is crucial to programming.
Setting variables in Mylange is simple. It takes three parts: the type, name,
and value. First goes the type, then the name, an arrow =>, then
the value.
For example, storing a name would look like this:
str code_name => "Mylange";
Conventionally, local variables are snake case (all lowercase, seperated by underscores).
Parameters are camal case (the first word lowercase, joined words are capitalized),
Classes are pascal case (like camal, but the first word is capitalized),
and global variables are upper-snake case (all uppercase, seperated by underscores).
The value of a variable must always match the decared type, or an error will be thrown.
Values can be reset in the same way as creating it, barring the type.
reset_var => "New Value";
You cannot use the resetting method to declare a varibale first.
Functions
Functions allow for code to really be useful. There are three things to any function:
the input, logic, and output. The inputs are called parameters, and the output is called
a return value.
To defined a function, the def keyword is used. Following is the return type,
name, then parameters enclosed in parenthesis. The parameters are simply a type followed
by the name. Then, the as keyword is used, before the logic.
Typically, the logic is confined in brackets ({}), but small functions
that only need a single line do not need them. Then, the return statement
is used, which ends the function.
Function do not always need parameters, not do they need returns. If you wish to
ommit a return, then the return type of the function should be nil.
Conventionally, all functions are pascal case.
Here are some examples of functions.
def int Add(int a, int b) as
return a + b;
def nil Greet() as {
str input => io.input("Name> ");
io.print("Hello " .. input .. "!");
};
Classes
Classes are used when simple types aren't enough. They can be used to store both
values and functions, called properties and methods, respectfully.
Classes also serve as types when creating a Casting out of a Class.
Main Statement
To create a class, the overall statement should look like this:
class CLS_NAME has ...;
It is also posible to build off another class, or extend it. This is done with the
extends keyword, which comes after the class name.
class CLS_NAME extends BASE_NAME has ...;
Modifiers
Everything inside of a class needs to have a visibility modifier. For now,
only Public visability is supported. This means that all properties and methods
can be accessed from the outsied of the class, and in code, all lines start with public.
When creating an extended class, there are also special modifiers. If a method or propery should be
redefined in the extended class, then the @override modifier comes first.
Properties
Properties serve as the storage mechanism in classes. They are much like the keys in a set.
To define a propterty, use the visability modifier, type, and name. Optionally, you can also
use an arrow to define a default value for the property. All properties that are not defined
will be nil upon creation.
public str Name;
public int Age => 0;
@override public int BaseProperty => 10; // Remember to use @override in extended classes for existing properties!
Properties can be accessed in two different ways, depending on where you are calling from.
Inside of a class, i.e. in a method, you need to use the this keyword. When a casting (an instance of a class)
method is called, this will be injected and be a reference to the casting being called on.
This allows you to call properties or methods, treating this as a casting.
When you have your casting (either the variable name outside or this inside), you can get and edit a property
as if it was a set. This means that a colon and the name are to be used to get it, or you can use a string inside square brackets
to get it. If the above properties where defined, then you could get them like:
this:Age;
casting:Name;
casting["BaseProperty"];
Keep in mind that private properties cannot be accessed via an external casting,
and must be accessed using this, which can only be obtained internally.
Methods
Just as properties store the info in a class, methods store the functionality. They are defined in the
same way as normal functions, just with the required modifiers in front.
Methods can be accessed similar to properties (using a casting name or this), just with the
dot extension and the required arguments. Again, only public methods can be accessed outside of the casting.
An important method that is critical to any class you wish to make a casting out of is the constructor.
This is defined as a method with the same name as the class. Without the constructor, you cannot
create a casting out of a class.
class Foo has {
public int Prop => 1;
public def nil Foo() as {};
// In this example, I do not wish for the constructor to do anything.
// However, it must be defined, or castings cannot be created.
public def int Bar(int u) as {
return u + this:Prop;
};
};
For extended classes, there is a very important modifier (used on its own) that you should use.
Extended classes work by copying over all the methods and properties exactly, and adding them
to a new class, different name. However, this means that the constructor is not correct. To
align them, use the @default constructor line. For our example above:
class TwoFoo extends Foo has {
@default constructor; // now, TwoFoo has the exact same constructor as Foo
...
};
Example
#include <io>;
class Person has {
public str Name;
public int Age;
public def str Person(str name, int age) as {
this:Name => name;
this:Age => age;
};
public def nil SayHi() as {
io.print(this:Name .. " says Hi!");
};
};
Person coder => new Person("Coder", 1);
coder:Name => "Coder2026";
coder.SayHi();
Operators
Operators are essential as they allow for the manipulation,
comparison, and logic of the programming langauge.
Operators have operation symbols (and sometimes word aliases) that are used
between a left and right value. The Types of these values are important,
as operators must be defined for each type. For example, adding can only happen between
numbers, "and" can only be done between booleans, and etc.
Below is a table outlining the operators, along with the type used.
At the top of the table are the operators with the highest precidence, meaning when
operators appear on the same level, which ones will be executed last (think PEMDAS).
| Left |
Operator |
Right |
Return |
Description |
bool |
&& |
bool |
bool |
Returns true if both the left and right are true, false elsewise.
An alias for && is and.
|
bool |
|| |
bool |
bool |
Returns true if either of the statements is true.
An alias for || is or.
|
any |
== |
any |
bool |
Returns true is the two values match.
|
any |
!= |
any |
bool |
Returns true is the two values do not match.
|
int|float |
< |
int|float |
bool |
Returns true if the left if less than the right.
|
int|float |
<= |
int|float |
bool |
Returns true if the left if less than or equal to the right.
|
int|float |
> |
int|float |
bool |
Returns true if the left if greater than the right.
|
int|float |
>= |
int|float |
bool |
Returns true if the left if greater than or equal to the right.
|
int|float |
+ |
int|float |
int|float |
Adds two numbers.
|
int|float |
- |
int|float |
int|float |
Subtracts two numbers.
|
int|float |
* |
int|float |
int|float |
Multiplies two numbers.
|
int|float |
/ |
int|float |
float |
Divides two numbers.
|
str|char |
.. |
str|char |
str |
Combines two strings together.
|
str |
.. |
int |
str |
Multiplies out a string by the right-hand int amount.
|
array |
.. |
str|char |
str |
Concatinates an array's elements using the string/char delimiter.
|
array |
.. |
array |
array |
Combines two arrays together.
|
In future versions of Mylange, it will be possible to add operations to classes,
allowing user control over what these operators do.
Controls
Control blocks are important for the flow of languages.
Mylange features the typical control blocks, which you can find below.
It is important to remeber that whenever the example uses an elipse with brackets
to represent the contents of a control block, it can be either a single-line
statement, or a brackets enclosed statement.
For all control blocks, the break keyword will cause the block to terminate.
The continue keyword is used to skip the rest of the logic, but the looping continues.
Do
do {...};
Do blocks are simple: they create another scope, and enter into it.
This is useful if you want to create variables that are short-lived,
as the interpreter will free all variables inside of the block
as soon as it leave.
#include <io>;
#include <file>;
do {
file.File f => new file.File("example.txt", "r");
io.print(f.read());
};
// f is no longer stored in memory.
// This is useful for large castings,
// or when a lot of variables are needed.
While
while (BOOLEAN) do {...};
While loops are simple blocks of code that have a single
boolean that gets re-evaluated everytime it loops.
int i => 0;
while (i < 10) do {
i => i + 1;
};
// Further code will be reached after i gets to 10.
If/Else
if (BOOLEAN) then {...};
if (BOOLEAN) then {...} else {...};
if (BOOLEAN) then {...} else if (BOOLEAN) then {...};
if (BOOLEAN) then {...} else if (BOOLEAN) then {...} else {...};
If statements are crucial for evaluating logic within code.
These are simple to write, involving just the if/else/then keywords,
and parenthesis-enclosed boolean logics. For example:
#include <io>;
int num => 10;
if (num == 10) then
io.print("Your number is 10")
else if (num == 5) then
io.print("Youre number is 5")
else io.print("I don't know your number");
Note: recall that semi-colon are always used after a statement. In if/else/then blocks,
they are not used inbetween ifs and elses. This is important; if it is used somewhere
in the middle, the code will break.
For
for (ITERABLE) do {...};
For loops are used to iterate over an Iterable, which is a special type
that denotes something that should be run over many values, and extracts those values.
The typical way to make an iterable is the in statement.
The in statement defines a looping variable, and takes in something to be iterated over.
For example,
int num in [1, 2, 3, 4, 5];
[str key, any value] in (name=>"Mylange", coolness=>10);
Brackets are used in Iterables if the thing being iterated can sperate the values. The only
currently supported way to do so is looping over sets key/value pairs.
#include <io>;
set langs => (python=>6, java=>2, cs=>8, mylange=>10);
for ([str name, int rating] in langs) do {
io.print("% got a %/10 rating!".format([name, rating]));
// See the string module for the <str>.format method
};