Mylange Docs

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
};
homeHome