Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

XMLang is an esolang, that is also valid XML. It is a fully-functional, turing-complete programming language, that features the basic things you would expect from a programming language, such as standard input/output, variables, functions, and control flow.

Interpreter

The interpreter is written in Rust, allowing it to be relatively fast and very portable. You can run it locally on your machine, or use the WebAssembly-powered online playground to try it out easily.

License

This project is open-source on GitHub and is licensed under the GNU GPL 3.0 license.

Local installation

The XMLang interpreter is written in Rust, and can be run locally on your machine.

GitHub Actions

build native binaries

The interpreter is built automatically on every commit using GitHub Actions.

You can get the latest built executables for your platform from this link.

Most likely you'll want to download these files:

Building from source

  1. Install Rust.

  2. Clone the repository:

    git clone https://github.com/GGORG0/xmlang.git
    
  3. Change to the project directory:

    cd xmlang
    
  4. Build the project:

    cargo build --release
    
  5. The built executable will be located in the target/release directory. You can run it with:

    ./target/release/xmlang examples/hello.xml
    

Online playground

The online playground is available at https://xmlang.ggorg.xyz.

It automatically saves your code in the browser's local storage, so you don't lose it.

Loading code from GitHub

Note: This will replace the code you saved in the browser's local storage.

Gist

The playground supports loading code from GitHub Gist.

The Gist has to contain a single XML file with your code. If it contains multiple files, the first one will be used.

To load a Gist, get its ID (the part after your username in the URL) and append it to the playground URL like this:

https://xmlang.ggorg.xyz/?gist=8da581666cbfd662d6d5fbbe8fce3ca7

The above URL will load the code from this Gist.

Repository

The playground also supports loading code from a file in a GitHub repository.

To load a file, append the repository owner, name, branch and the path to the file to the playground URL like this:

https://xmlang.ggorg.xyz/?owner=GGORG0&repo=xmlang&branch=master&file=examples/hello.xml

The above URL will load the code from this file.

Stack

  • WebAssembly (via Wasmer.js) - allows running the XMLang interpreter in the browser inside a full WASI environment with a virtual filesystem.
  • CodeMirror - code editor
  • Xterm.js - terminal emulator
  • Vite - build tool

Building

  1. Install the following dependencies:

    • Node.js
    • Pnpm (corepack enable)
    • Rust (with the wasm32-wasip1 target: rustup target add wasm32-wasip1) - to build the WebAssembly module
    • mdBook (optional) - to build the documentation
  2. Clone the repository:

    git clone https://github.com/GGORG0/xmlang.git
    
  3. Change to the project directory:

    cd xmlang
    
  4. Build the WebAssembly module (optional - this will be done automatically by Vite during the build):

    cargo build --release --target wasm32-wasip1
    
  5. Build the documentation (optional):

    mdbook build
    

    The documentation will be built in the book directory.

  6. Change to the playground directory:

    cd playground
    
  7. Install the dependencies:

    pnpm install
    
  8. Build the project:

    pnpm build
    

    Or start the development server:

    pnpm dev
    

Language introduction

All XMLang code is valid XML, and can be parsed by any XML parser.

Comments are standard XML comments, and are ignored by the parser:

<!-- This is a comment -->
<element attribute1="value1" attribute2="value2">
    <child1>Child 1 content</child1>
    <child2>Child 2 content</child2>
    <!-- This is another comment -->
</element>

Note: Support for CDATA sections and entity references (for example &lt;) is not implemented yet.

Elements

Everything in XMLang is an XML element.

An element is a tag in the XML document, which can have attributes, children, and text content.

<element attribute1="value1" attribute2="value2">
    <child1>Child 1 content</child1>
    <child2>Child 2 content</child2>
</element>

Attributes

Attributes are defined in the opening tag of an element, and are used to provide constant, static values to the element. They can't be dynamically generated or changed at runtime.

Attribute values are always strings by default, but certain built-in elements parse them as other types.

Children

Children are elements that are nested inside another element.

Some elements accept 0, 1, a predefined number, or an arbitrary number of children.

Every child element of an expression is considered an argument to that expression, and the order of the children matters. The children are evaluated in the order they appear in the XML document, and the result of each child is passed to the parent element as an argument.

An element can also be self-closing, as defined by the XML standard:

<element attribute1="value1" attribute2="value2" />

That is equivalent to:

<element attribute1="value1" attribute2="value2"></element>

Text content

Text content is the text that appears between the opening and closing tags of an element.

It is always considered a string, and has to be converted to other types if needed.

You can mix text content with other child elements, but due to parser limitations, the whitespace between the text and the child elements is not preserved, and will be ignored.

<element attribute1="value1" attribute2="value2">
    Text content
    <child1>Child 1 content</child1>
    <child2>Child 2 content</child2>
    Other text content
</element>

If you want to preserve whitespace, you can use tricks like the <space /> element or the <join> element.

<program>
    <set var="myVar">wonderful</set>
    <print>Hello <space /> <get var="myVar" /> <space /> world!</print>
</program>

When mixing text content with child elements, each text node and child element is considered a separate child of the parent element, and the order of the children matters. If you want to concatenate text content with child elements, keeping them as 1 string child, you can use the <string> element to wrap the text content and child elements together.

<element attribute1="value1" attribute2="value2">
    <string>Text content <child1>Child 1 content</child1> Other text content</string>
</element>

Internally each text content node is represented as an unnamed element (<></>), with the text as its _text attribute.

Expressions and statements

There are 2 main types of elements in XMLang: expressions and statements. Almost everything is an expression.

The 2 are distinguished by the fact that statements contain children that aren't valid elements anywhere else in the language, for example, the <if> statement contains <condition>, <then>, and optionally <else> elements, which are not valid outside an <if> statement.

Every element returns a value, which can be used by its parent element. If an element does not return a value, it is considered to return null.

Data types

XMLang has several built-in data types.

All text content in XMLang is considered a string, and can be converted to other types as needed. For example, these are different:

<set var="n"><int>42</int></set>

<print>
    <add>
        <get var="n" />
        1
    </add>
</print> <!-- prints 421 -->

<print>
    <add>
        <get var="n" />
        <int>1</int>
    </add>
</print> <!-- prints 43 -->

XMLang supports the following data types:

  • null: Represents the absence of a value.
  • int: Represents an integer value.
  • float: Represents a floating-point number.
  • bool: Represents a boolean value, either true or false.
  • string: Represents a UTF-8 encoded string of text.

There are no lists, arrays, dictionaries, objects, classes, or other complex data types in XMLang.

<type>

The <type> element is used to get the type name of a value as a string. If multiple children are present, their types are concatenated with a space.

<type>42</type> <!-- int -->
<type>3.14</type> <!-- float -->
<type>true</type> <!-- bool -->
<type>hello</type> <!-- string -->

<type>
    <int>42</int>
    <float>3.14</float>
    <true />
    hello
</type> <!-- int float bool string -->

Null

Type name: null

Rust type: ()

This is the most basic data type, representing the absence of a value.

<null />

Conversion to other types

When converting null to other types, it converts to the following values:

<unwrap>

The <unwrap> element is used to throw an error if its child is null. If the child is not null, it returns the child value.

It optionally receives the message attribute, which is used as the error message if the child is null.

<unwrap><null /></unwrap> <!-- Error: "Unwrapped value is null" -->
<unwrap message="Custom error message"><null /></unwrap> <!-- Error: "Custom error message" -->
<unwrap><int>42</int></unwrap> <!-- 42 -->

Integer

Type name: int

Rust type: i64

An integer is a whole number, which can be positive, negative, or zero. Under the hood, integers are represented as 64-bit signed integers.

<int>42</int>
<int>-7</int>
<int>0</int>

Conversion to other types

When converting an int to other types, it behaves exactly as you would expect:

  • float: A float with the same value and a fractional part of .0.
  • bool: true if the value is non-zero, false if it is zero.
  • string: The string representation of the integer, e.g., the string 42.

Float

Type name: float

Rust type: f64

A float is a number that can have a fractional part, represented in decimal notation. Under the hood, floats are represented as 64-bit double-precision floating-point numbers.

<float>1.0</float>
<float>3.14</float>
<float>-2.71828</float>
<float>0.0</float>

Conversion to other types

When converting a float to other types, it behaves exactly as you would expect:

  • int: An integer with the same value, discarding the fractional part (always rounding down).
  • bool: true if the value is non-zero, false if it is zero (0.0).
  • string: The string representation of the float, e.g., the string 42.5.

Boolean

Type name: bool

Rust type: bool

A boolean is a data type that can have one of two values: true or false.

<bool>true</bool>
<bool>yes</bool>
<bool>anything</bool>
<true />

<bool>false</bool>
<bool>no</bool>
<false />

Conversion to other types

When converting a bool to other types, it converts to the following values:

  • int: 1 for true, 0 for false.
  • float: 1.0 for true, 0.0 for false.
  • string: The string representation, which is always true or false.

String (and string operations)

Type name: string

Rust type: String

A string is a UTF-8 encoded sequence of characters.

All text content in XMLang is considered a string, and wrapping it in a <string> element is optional, although sometimes it's necessary for, for example, concatenation - it ensures that the value is treated as a single string, rather than multiple children.

All children of a <string> element are evaluated, converted to strings and then concatenated into a single string (without any separators). Null values are ignored.

<string>Hello, world!</string>
<string>42</string>
<string>3.14</string>
<string>true</string>
<string>false</string>
<string />

Conversion to other types

When converting a string to other types, it behaves as follows:

  • int: Parses the string as an integer. If the string is not a valid integer, it will throw an error.
  • float: Parses the string as a float. If the string is not a valid float, it will throw an error.
  • bool: Converts the string to a boolean value, where all values except for the following are considered true:
    • false
    • 0
    • off
    • no
    • empty string

<space />

The <space /> element returns a string with a single space character.

Attributes

  • count (int, optional): Specifies the number of space characters to return. Defaults to 1.

Example

<space /> <!-- " " -->
<space count="3" /> <!-- "   " -->

<print>
    <string>Hello,</string>
    <space />
    <string>world!</string>
</print> <!-- prints "Hello, world!" -->

<join>

The <join> element concatenates multiple strings into a single string, with a separator.

Attributes

  • separator (string, optional): A string to insert between each child string (default is a single space).
  • start (string, optional): A string to prefix the result with (default is an empty string).
  • end (string, optional): A string to suffix the result with (default is an empty string).

Children

All its children get evaluated, and then converted to strings before concatenation.

Example

<join end="!">
    <int>1</int>
    <string>Hello</string>
    <string>world</string>
</join> <!-- "1 Hello world!" -->

<trim>

The <trim> element removes leading and trailing whitespace from a string.

Attributes

  • start (bool, optional): Whether to trim leading whitespace. Defaults to true.
  • end (bool, optional): Whether to trim trailing whitespace. Defaults to true.

Children

It accepts exactly 1 child, which is the string to trim. The child is evaluated and converted to a string before trimming.

Example

<trim>   Hello, world!   </trim> <!-- "Hello, world!" -->
<trim start="true">   Hello, world!   </trim> <!-- "Hello, world!   " -->
<trim end="true">   Hello, world!   </trim> <!-- "   Hello, world!" -->
<trim start="true" end="true">   Hello, world!   </trim> <!-- "Hello, world!" (default) -->
<trim start="false" end="false">   Hello, world!   </trim> <!-- "   Hello, world!   " (why?) -->

<starts-with>

The <starts-with> element checks if a string starts with a given prefix, and returns a boolean value.

Children

It accepts exactly 2 children - the first is the string to check, and the second is the prefix to check against.

Example

<starts-with>
    <string>Hello, world!</string>
    <string>Hello</string>
</starts-with> <!-- true -->
<starts-with>
    <string>Hello, world!</string>
    <string>world</string>
</starts-with> <!-- false -->
<starts-with>
    <string>Hello, world!</string>
    <string>hello</string>
</starts-with> <!-- false (case-sensitive) -->

<ends-with>

The <ends-with> element checks if a string ends with a given suffix, and returns a boolean value.

It is similar to <starts-with>, but checks the end of the string instead.

Children

It also accepts exactly 2 children - the first is the string to check, and the second is the suffix to check against.

Example

<ends-with>
    <string>Hello, world!</string>
    <string>world!</string>
</ends-with> <!-- true -->
<ends-with>
    <string>Hello, world!</string>
    <string>Hello</string>
</ends-with> <!-- false -->
<ends-with>
    <string>Hello, world!</string>
    <string>WORLD!</string>
</ends-with> <!-- false (case-sensitive) -->

<contains>

The <contains> element checks if a string contains a given substring, and returns a boolean value.

It is similar to <starts-with> and <ends-with>, but checks if the string contains the substring anywhere within it.

Children

It accepts exactly 2 children - the first is the string to check, and the second is the substring to check against.

Example

<contains>
    <string>Hello, world!</string>
    <string>world</string>
</contains> <!-- true -->
<contains>
    <string>Hello, world!</string>
    <string>hello</string>
</contains> <!-- false (case-sensitive) -->
<contains>
    <string>Hello, world!</string>
    <string>!</string>
</contains> <!-- true -->

<program>

Every XMLang program has to be a valid XML document. The root element of the document is <program>, which contains the program's code.

This is the simplest XMLang program, which prints Hello, world! to the standard output:

<program>
    <print>Hello, world!</print>
</program>

The <program> element can contain any number of elements, which are executed in the order they appear in the document. It is a block - <return> can be used to stop its execution. The returned value of the <program> element is discarded.

The <program> element is only valid as the root element of the document. It cannot be used anywhere else in the document.

Input/output

<print>

Outputs text to the standard output.

Attributes

  • newline (bool, optional): Whether to print a newline after the output. Defaults to true. Useful for reading input from the user or printing something in chunks. If set to false, the next output will continue on the same line.

Children

All its children are evaluated, converted to strings and concatenated together (without any separators), in the order they appear in the XML document.

Example

<program>
    <print>Hello, world!</print>
    <print>Sum of 2 and 3 is: <space /> <add><int>2</int><int>3</int></add></print>
    <print newline="false">This is printed without a newline. <space/></print>
    <print>And this is printed on the same line.</print>
</program>

This will output:

Hello, world!
Sum of 2 and 3 is: 5
This is printed without a newline. And this is printed on the same line.

<readline />

Reads a line of input from the user (until the 0xA newline character is reached) and returns it as a string.

The returned value is trimmed of trailing CR (carriage return) and LF (line feed) characters, so it can be used directly without further processing. Further trimming has to be done manually with <trim> if needed.

It doesn't accept any attributes or children.

Variables

XMLang supports variables, which are used to store data that can be referenced and manipulated throughout the program.

Except for functions, there is 1 global scope in the entire program. Each function execution has its own local scope, which can't access or modify variables in the global scope.

Variables can have any string name, with no restrictions on characters (even spaces are allowed!) or length. However, it's highly recommended to use a proper casing style, with only letters and numbers.

Variables can store values of any data type. The value and type of a variable can be changed at any time, and the new value will be used in subsequent operations.

Example

<program>
    <set var="x"><int>5</int></set>
    <set var="y"><float>3.14</float></set>
    <set var="message">Hello, world!</set>
    <print>Value of x: <space /> <get var="x" /></print>
    <print>Value of y: <space /> <get>y</get></print>
    <print>Message: <space /> <get var="message" /></print>
    <print>This variable does not exist: <space /> <get var="non_existent" /></print>
    <print>And neither does this one: <space /> <get var="also_non_existent"><int>42</int></get></print>
</program>

<set>

The <set> element is used to assign a value to a variable.

Attributes

  • var (string): The name of the variable to set. If the variable does not exist, it will be created.

Children

It accepts a single child, which is evaluated and the resulting value is assigned to the variable.

<get>

The <get> element is used to retrieve the value of a variable.

Attributes

  • var (string, optional): The name of the variable to retrieve.

Children

If the var attribute has been provided, <get> optionally accepts a single child, which is used if the variable does not exist. If the variable exists, this child is ignored. If the variable does not exist and no child is provided, null is returned.

If the var attribute has not been provided, <get> must have a single child, which is evaluated and converted to a string. This child is used as the variable name to retrieve. If the variable does not exist, null is returned.

Blocks

Blocks are elements, that execute their children in the order they appear, and return the result of the last child as their own result.

Blocks do not have their own scope, meaning that variables set inside a block are accessible outside of it, and variables set outside of a block are accessible inside it.

Many elements in XMLang are blocks, such as <program>, <block>, <loop>, ;<if>'s children, <try>'s children and functions.

<block>

The <block> element is used to create a block of code that can be executed.

This is the simplest way to create a block, and it can be useful when you want to calculate a value inside another element's children, while returning a single value.

If no children are provided, the result of the block is null.

Example

<program>
    <print>
        The result of the block is:
        <space />
        <block>
            <set var="x"><int><readline /></int></set>
            <set var="x"><add><get var="x" /><int>1</int></add></set>
            <get var="x" /> <!-- Only the result of this child will be returned -->
        </block>
    </print>
</program>

<return>

The <return> element is used to return a value from a block or a function.

It stops the execution of the nearest block or function and returns a value to the caller.

No further elements after <return> are executed.

Children

It accepts at most a single child, which is evaluated and returned as the result of the block or function.

If no child is provided, the result of the block or function is null.

Example

<program>
    <print>
        The result of the block is:
        <space />
        <block>
            <set var="x"><int><readline /></int></set>
            <set var="x"><add><get var="x" /><int>1</int></add></set>
            <return><get var="x" /></return>
            <print>This will never be printed.</print>
        </block>
    </print>
</program>

Specials

Specials are constants set by a containing element.

Examples of specials include the error caught by a <try> element, or the attributes and children passed to a function.

<special>

The <special> element is used to retrieve the value of a special.

If the special of the requested name, the Special `{name}` not found error is thrown.

Attributes

  • name (string, optional): The name of the special to retrieve.

Children

If the name attribute has been provided, <special> does not accept any children. The value of the special with the given name is returned.

If the name attribute has not been provided, <special> must have a single child, which is evaluated and converted to a string. This child is used as the name of the special to retrieve.

Error handling

Errors in XMLang work similarly to errors in many other programming languages.

They can be thrown (either manually or by a built-in operation) and caught using the <try> and <catch> elements.

When an error is thrown, the program execution stops and control is transferred to the nearest <catch> element that can handle the error. If no <catch> element is found, the program execution stops and the error is printed to the standard output.

<throw>

The <throw> element is used to throw an error.

Attributes

  • message (string, optional): The error message to throw.

Children

If the message attribute has been provided, <throw> does not accept any children. The provided message is used as the error message.

If the message attribute has not been provided, <throw> can optionally have children, which are evaluated, converted to strings and concatenated, just like in the <print> element.

  • If at least 1 child are provided, the resulting string is used as the error message.
  • If no children are provided or the constructed error message is empty, the error message is set to An error occurred, but no message was provided..

<try>

The <try> element is a statement that allows you to execute a block of code that may throw an error. If an error is thrown, control is transferred to the nearest <try> statement's <catch> element.

Children

This is a statement. The only elements that can be direct children of <try> are the <do> and <catch> elements, which aren't valid elements anywhere else.

<do>

The <do> element is used to define a block of fallible code that will be executed when the <try> statement is reached. It is a block.

If an error is thrown during the execution of the <do> block, control is transferred to the nearest <catch> element.

<catch>

The <catch> element is used to handle errors thrown by the <do> block. It is a block.

Specials

The <catch> element can access the error that was thrown by the <do> block using the <special> element with the name attribute set to error. This will return the error as a string.

Example

<program>
    <try>
        <do>
            <print>
                The meaning of life is
                <space />
                <div>
                    <int>42</int>
                    <int>0</int> <!-- Oh no! Division by zero! -->
                </div>
            </print>
        </do>
        <catch>
            <print>
                An error occurred:
                <space />
                <special name="error" />
            </print> <!-- Prints: "An error occurred: Division by zero is not allowed" -->
        </catch>
    </try>
</program>

Mathematical operations

The compatibility tables below represent the actions taken by the interpreter when the operation is applied to two values of the given types. anything means that the operation can be applied to any data type that wasn't explicitly listed above in the list (the list is evaluated from top to bottom, so the first matching type is used).

Folding operations

Addidion, subtraction, multiplication, division, and modulo in XMLang accept any number of children.

They are evaluated and the result is calculated by using the popular fold iterator method.

The first child's value is used as the initial value and stored in an accumulator. Then, each subsequent child's value is used to update the accumulator by applying the operation. The final value of the accumulator is returned as the result.

If no children are provided, the result is null.

If the two data types of an operation are incompatible, an error (Can't {operation} incompatible types: {type1} and {type2}) is thrown.

Example

<program>
    <print>
        <add>
            <int>1</int>
            <float>2.1</float> <!-- 3.1 -->
            <int>3</int> <!-- 6.1 -->
            <string>a</string> <!-- "6.1a" -->
        </add>
    </print> <!-- prints 6.1a -->
</program>

<add>

The order of the children does not matter, as addition is commutative.

Compatible types:

<sub>

The order of the children matters, as subtraction is not commutative.

Incompatible types:

Compatible types:

<mul>

The order of the children does not matter, as multiplication is commutative.

Incompabile types:

Compatible types:

<div>

The order of the children matters, as division is not commutative.

Division by zero will throw the error Division by zero is not allowed.

Incompatible types:

Compatible types:

<mod>

The order of the children matters, as modulo is not commutative.

Modulo by zero will throw the error Division by zero is not allowed.

Incompatible types:

Compatible types:

Unary operations

Arithmetic negation and absolute value in XMLang accept a single child.

If the data type of an operation is incompatible, an error (Can't {operation} incompatible type: {type}) is thrown.

<neg>

The <neg> element is used to compute the additive inverse of a value.

Incompatible types:

Compatible types:

<abs>

The <abs> element is used to calculate the absolute value of a number.

Incompatible types:

Compatible types:

Logical operations

<not>

The <not> element is used to negate a boolean value.

Children

It only accepts a single child, which is evaluated and converted to a boolean. After evaluation, the boolean value is negated (i.e. true becomes false, and false becomes true).

Example

<program>
    <print>
        <join>
            <not><bool>true</bool></not> <!-- false -->
            <not><int>1</int></not> <!-- false -->
            <not><float>0.0</float></not> <!-- true -->
            <not>hello</not> <!-- false -->
            <not><null /></not> <!-- true -->
        </join>
    </print>
</program>

<and>

The <and> element is used to perform a logical AND operation on at least two boolean values.

Children

It accepts at least two children, which are evaluated and converted to booleans. The result is true if all children evaluate to true, and false otherwise.

Example

<program>
    <print>
        <join>
            <and>
                <bool>true</bool>
                <bool>false</bool>
            </and> <!-- false -->
            <and>
                <int>1</int>
                <float>1.0</float>
            </and> <!-- true -->
            <and>
                <bool>true</bool>
                <not><null /></not>
                <string>hello</string>
            </and> <!-- true -->
        </join>
    </print>
</program>

<or>

The <or> element is used to perform a logical OR operation on at least two boolean values.

Children

It accepts at least two children, which are evaluated and converted to booleans. The result is true if at least one child evaluates to true, and false otherwise.

Example

<program>
    <print>
        <join>
            <or>
                <bool>true</bool>
                <bool>false</bool>
            </or> <!-- true -->
            <or>
                <int>0</int>
                <float>0.0</float>
            </or> <!-- false -->
        </join>
    </print>
</program>

<eq>

The <eq> element is used to check if all of at least two values are equal.

Two values are considered equal if they have the same type and value.

Children

It accepts at least two children. The result is true if all children evaluate to the same value, and false otherwise.

Example

<program>
    <print>
        <join>
            <eq>
                <int>42</int>
                <int>42</int>
            </eq> <!-- true -->
            <eq>
                <int>3</int>
                <float>3.0</float>
            </eq> <!-- false -->
            <eq>
                <bool>true</bool>
                <not><null /></not>
                <bool><string>hello</string></bool>
            </eq> <!-- true -->
            <eq>
                <null />
                <null />
            </eq> <!-- true -->
        </join>
    </print>
</program>

<ne>

The <ne> element is used to check if none of at least two values are equal.

Children

It accepts at least two children. The result is true if all children evaluate to different values, and false otherwise.

Example

<program>
    <print>
        <join>
            <ne>
                <int>42</int>
                <int>42</int>
            </ne> <!-- false -->
            <ne>
                <int>3</int>
                <float>3.0</float>
            </ne> <!-- true -->
            <ne>
                <bool>true</bool>
                <not><null /></not>
                <bool><string>hello</string></bool>
            </ne> <!-- false -->
            <ne>
                <bool>true</bool>
                <string>true</string>
                <not><null /></not>
            </ne> <!-- true -->
        </join>
    </print>
</program>

<gt> (>), <ge> (≥), <lt> (<), <le> (≤)

The <gt>, <ge>, <lt>, and <le> elements are used to compare at least two values.

Children

These elements accept at least two children. The result is true if all overlapping pairs of children evaluate to values that meet the specified comparison, and false otherwise.

For example, when using <gt> with 4 children (a, b, c, d), it returns true if (a > b AND b > c AND c > d).

The order of the children matters, as the comparisons are made sequentially.

Uncomparable data types

Comparisons are done using the Rust PartialOrd trait, which allows for uncomparable values. This means that if you try to compare two incompatible types (e.g., an integer and a string), the result will be false, no matter the comparison operator used.

This can be counterintuitive, see the example (the second child of the comparison elements is a string, which is not comparable to an integer):

<program>
    <print>
        <join>
            <ge>
                <int>42</int>
                42
            </ge> <!-- false -->
            <eq>
                <int>42</int>
                42
            </eq> <!-- false -->
            <le>
                <int>42</int>
                42
            </le> <!-- false -->
        </join>
    </print>
</program>

Data type compatibility

This compatibility list is similar to the one used for mathematical operations.

<if>

The <if> element is a conditional statement that allows you to execute a block of code based on whether a condition is true or false.

Children

This is a statement. The only elements that can be direct children of <if> are the <condition>, <then>, <elif>, and <else> elements, which aren't valid elements anywhere else.

<condition>

The <condition> element is used to define the condition that will be evaluated to determine whether the <then> block should be executed.

There must be exactly 1 <condition> element as a child of <if>/<elif>.

It must have a single child, which is evaluated and converted to a boolean. If the result is true, the <then> block will be executed.

<then>

The <then> element is used to define a block of code that will be executed if the <condition> evaluates to true. It is a block.

There must be exactly 1 <then> element as a child of <if>/<elif>.

Specials

The <then> element can access the result of the <condition> evaluation using the <special> element with the name attribute set to condition. This will return the result as the type of the value returned by the child of <condition> (but before being converted to a boolean).

<elif>

The <elif> element is used to define an additional condition that will be evaluated if the <condition> evaluates to false and all previous <elif> conditions also evaluated to false. It is a statement.

There can be any number of <elif> elements as children of <if>.

Each <elif> must have exactly 1 <condition> and 1 <then> element as children.

<else>

The <else> element is used to define a block of code that will be executed if the <condition> evaluates to false and all <elif> conditions also evaluated to false. It is a block.

There can be at most 1 <else> element as a child of <if>. If present, it must be the last child of <if>.

Example

<program>
    <set var="secret"><int>42</int></set>
    <set var="guess"><int><readline /></int></set>
    <if>
        <condition>
            <eq>
                <get var="guess" />
                <get var="secret" />
            </eq>
        </condition>
        <then>
            <print>Congratulations! You guessed the secret number!</print>
        </then>
        <elif>
            <condition>
                <lt>
                    <get var="guess" />
                    <get var="secret" />
                </lt>
            </condition>
            <then>
                <print>Your guess is too low. Try again!</print>
            </then>
        </elif>
        <else>
            <print>Your guess is too high. Try again!</print>
        </else>
    </if>
</program>

<loop>

The <loop> element is used to create loops in the program. It allows you to repeat a block of code multiple times.

It's similar to the for loop in many other programming languages.

Attributes

  • start (int, optional): The starting value of the loop counter. If not provided, it defaults to 0.
  • end (int, optional): The ending value of the loop counter. If not provided, the loop will run indefinitely until a <return> or <exit /> element is encountered, or an unhandled error is thrown.

The loop will run from start to end - 1, incrementing the loop counter by 1 on each iteration.

If the loop terminates due to the counter reaching end - 1, the result of the loop is null.

Children

This is a block.

Its children will be executed on each iteration of the loop.

Specials

The <loop> element can access the current loop counter using the <special> element with the name attribute set to iteration. This will return the current value of the loop counter as an int.

<continue />

The <continue /> element is used to skip the rest of the current iteration and move to the next iteration of the loop.

It is only valid inside a <loop> block. When used anywhere else, it will throw an error (Tried to continue outside of a loop).

<return>

Similarly to other blocks, the <return> element is used to exit the loop and return a value to the caller.

Example

<program>
    <set var="sum"><int>0</int></set>
    <loop start="1" end="6">
        <set var="sum">
            <add>
                <get var="sum" />
                <special name="iteration" />
            </add>
        </set>
        <print>Current iteration: <space /> <special name="iteration" /></print>
    </loop>
    <print>Total sum: <space /> <get var="sum" /></print> <!-- Prints: "Total sum: 15" -->
</program>

Functions

XMLang supports functions, which are reusable blocks of code.

Functions can take attributes and children, which are used as parameters. They can return a value, which can be used in the calling code. They can be called from anywhere in the program, including inside other functions, provided they are defined before the call.

Unlike anywhere else in the program, functions have their own local scope. They can't access or modify variables in the global scope, and values of the local variables defined in the function do not persist between function calls.

<function>

The <function> element is used to define a function.

Attributes

  • name (string): The name of the function.

Children

This is a block. Its children are saved and will be executed when the function is called.

<call>

The <call> element is used to call a function.

Attributes

  • name (string): The name of the function to call. If the function does not exist, an error will be thrown (Function `{name}` not found).
  • Any other attributes: see specials below.

Children

It accepts any number of children, which are evaluated and passed as parameters to the function. See specials below.

Specials

The body of the function (children of <function>) can access the attributes and children passed to the function using the <special> element with the name attribute set to:

  • The name of the attribute passed to the <call> element to retrieve the value of that attribute as a string.
  • child_count to retrieve the number of children passed to the <call> element as an int.
  • child:{index} to retrieve the value of the child at the specified index (int). The index is zero-based, so the first child is child:0, the second child is child:1, and so on.

Example

<program>
    <function name="greet">
        <set var="person">
            <if>
                <condition>
                    <eq>
                        <special name="child_count" />
                        <int>0</int>
                    </eq>
                </condition>
                <then>
                    <special name="person" />
                </then>
                <else>
                    <special name="child:0" />
                </else>
            </if>
        </set>
        <print>Hello, <space /> <get var="person" />!</print>
    </function>

    <call name="greet" person="Alice" />
    <call name="greet">Bob</call>
</program>

<exit />

The <exit /> element is used to terminate the program immediately.

Attributes

  • code (int, optional): The exit code of the program. If not provided, the default exit code is 0.

Example

<program>
    <print>Exiting the program...</print>
    <exit code="1" />
    <print>This line will not be executed.</print>
</program>

<delay>

The <delay> element is used to pause the execution of the program for a specified duration.

Attributes

  • duration (int, optional): The duration in milliseconds for which the program should be paused.

Children

If the duration attribute has been provided, it does not accept any children. The program will pause for the specified duration.

If the duration attribute has not been provided, it must have a single child, which is evaluated and converted to an int. This value is used as the duration for which the program should be paused.

Example

<program>
    <print>Waiting for 1 second...</print>
    <delay duration="1000" />
    <print>Waiting for 2 seconds...</print>
    <delay>
        <mul>
            <int>2</int>
            <int>1000</int>
        </mul>
    </delay>
    <print>Hello, world!</print>
</program>

<rand />

The <rand /> element is used to generate a random integer within a specified range.

Attributes

  • min (int, optional): The minimum value of the range (inclusive). Defaults to 0.
  • max (int, optional): The maximum value of the range (inclusive). Defaults to Rust's i64::MAX.

Example

<program>
    <print>
        Random number between 1 and 10: <space /> <rand min="1" max="10" />
    </print>
</program>