Skip to content
Rusketh edited this page Apr 3, 2024 · 47 revisions

Expression Three

Syntax

Work in progress

The compiler for Expression 3 is a huge work in progress and there for the syntax of this language is likely to change over the course of its development.

Similarities

Expression 3 uses a language based on expression advanced 2 but is closer to c#, it is often described as a lua and c# mash up.

Directives

The expression 3 language contains directives that work almost the same way as they did in e2. All directives are prefixed with the @ character and should appear before any statements or expressions in your code. Unlike E2 directives can appear after comments.

Name directive

This directive is used to set the name of the Gate the code has been uploaded to.

@name "Example - E3";

Model directive

This directive is used to set the model of any newly created chip using this code.

@model "models/nezzkryptic/e3_chip.mdl";

Include directive

This directive is used to include additonal code to your script. This can be very useful when creating libraries and user classes to be used across multiple gates.

@include "example_include";

The above example will include the script located at data/golem/example_include.txt

Wiremod imports and outports

Just like in E2, expression 3 uses directives for creating wiremod inputs and outputs. These directives do not follow the usual rules and can appear any where in your code.

@input number A, B, C;

The above example creates 3 variables A, B, and C of type number, these variables will be updated when the input of the same name is changed.

@output number A, B, C;

The output directive is basically the same as the input directive how ever the wire output will be updated a triggered at the end of the execution with the value of the corresponding variable.

If either of these 2 directives appear inside a nested block (e.g if statement) the variables created will also be nested to that block.

Wired input and outputs are considered synced variables when defined in shared space. Wiremod inputs & outputs can not be defined client side and should always appear inside a server side code block when synchronisation is not necessary.

Synced Variables

Because E3 can run both server & client side, variables defined on the server side are not accessible client side. To alleviate this the Synced directive has been added.

@synced number A, B, C;

Synced variables must only be defined in shared space and act as global variables. Synced variables can only be read client side and can only be assigned server side. Synced variables are synced every second the gate is running but synchronisation is not always guaranteed.

Synced variables use net messages and should be used sparingly.

Comments

Expression 3 support 2 styles of comments. Single line

//Example of a single line comment

Multi line

/* Example
of a multi line
comment */

Blocks

In expression 3 a block of code is wrapped in curly brackets.

{
    // Example
}

These brackets can be omitted causing only the first line to be used as the block.

Statements

If Statement

An if statement is a conditional statement that will execute a block of code only under certain conditions.

if (Condition) block
elseif (Condition) block
else block`

if (false) {
    debug.print(0)
} elseif (false) {
    debug.print(1)
} else {
    debug.print(2)
}

Server and Client Statement

These statements will only execute a block of code based on if the code is running server side or client side.

server block:

server {
    debug.print("server");
}

client block:

client {
    debug.print("client");
}
Server and Client Directives

You can also use @server or @client directives to define server side or client side only gates. These must be called at the top of your code and disable the use of server and client code blocks.

@server;


@client;

Assignments

All assignment statements in expression 3 can be used to assign one or more values at once.#

Definition statement

When defining a variable it is important to note that it is nested to its current block.

class variable[, variable]* = expression[, expression]*;

number a = 7;
string first, last = "John", "Doe";
Global definition statement

Same as a definition statement except defines a global variable, these variables are not nested to there current block.

global class variable[, variable]* = expression[, expression]*;

global number a = 7;
global string first, last = "John", "Doe";
Assignment statement

Assignments are used to assign a value to an existing variable.

variable[, variable]* = expression[, expression]*;

a = 7;
first, last = "John", "Doe";
Arithmetic assignment statement

Arithmetic assignments are used to perform an arithmetic operation on a variable and then assign the result of that operation to the variable.

Additon
 variable[, variable]* += expression[, expression]*;

 exampleVar, anotherVar += 100, 200;
Subtraction
 variable[, variable]* -= expression[, expression]*;

 exampleVar, anotherVar -= 100, 200;
Division
 variable[, variable]* \= expression[, expression]*;

 exampleVar, anotherVar \= 100, 200;
Multiplication
 variable[, variable]* *= expression[, expression]*;

 exampleVar, anotherVar *= 100, 200;

User Function statement

Expression 3 supports user defined functions.

function type variable([type parameter,]*) block

function int example(int a, int b) {
    return a + b;
}

Delegates

Delegates are function templates and are used by the compiler to retain information about a functions parameters as well as its return values. This will be covered in depth in a later section.

delegate type variable([type,]*) {
    return number
}

delegate string example(string, string) {
    return 1
}

The number that appears after return is the amount of values returned of type.

Inline Functions

Expression 3 supports 2 types of inline functions as expressions.

function(int a, int b) { return a + b; }

(int a, int b) => { return a + b; }

These can be used to define a function with in a statment.

timer.Simple("test", 1, 1, () => { system.print("timer"); });

Expressions

Operators

Ternary (Conditional)
Expression ? Expression : Expression
Logical Or
Expression || Expression
Logical And
Expression && Expression
Binary Xor
Expression ^^ Expression
Binary Or
Expression | Expression
Binary And
Expression & Expression
Binary And
Expression & Expression
Equal To
Expression == Expression
Not Equal To
Expression != Expression
Equal To One Or More

This is a nifty addition to expression 3 that allows you to compare if 1 value is equal to any of a list of values.

Expression == [[Expression,*]]

1 == [1, 2, 3]
Not Equal To All

Just like the previous this allows you to compare if 1 value not is equal to any of a list of values.

Expression != [[Expression,*]]
Grater Than
Expression > Expression
Less Than
Expression < Expression
Grater Or Equal To
Expression >= Expression
Less or Equal To
Expression <= Expression
Binary Shift Left
Expression << Expression
Binary Shift Right
Expression >> Expression
Addition
Expression + Expression
Subtraction
Expression - Expression
Division
Expression / Expression
Multiplication
Expression * Expression
Exponent
Expression ^ Expression
Modulus
Expression % Expression
Positive
+Expression
Negative
-Expression
Not
!Expression
Length
#Expression
Cast

The casting operator takes a class. The Compiler will then try to convert the value on the right to that class.

(Class) Expression

number i = (number) 101;

Constructors

A Constructor is used to create an object.

new Class([expression,]*)

new vector.3d(1, 1, 1)
Call Operator

In expression 3 some objects can be called, user functions being the main one.

Value([Expression,]*)

//No good examples currently exist.

Functions and Libraries

In expression 3 all functions exist with in a library.

Library.Function([Expression,]*)

debug.print(278)

Methods

Value.Method([Expression,]*)

"string".find("s")

Events

Events are lambda based callbacks that are used to respond to events outside the main execution. Events work similar to the hook system in gmod lua.

event.add(string event name, string callback id, function callback )

event.add("Think", "example", function() { system.print("think") })

Similarly you can remove event call backs

event.remove(string event name, string callback id)

event.remove("Think", "example")

User Classes

E3 provides a user class system, this means that anybody can create an object suited around their needs.

Example Class

Lets look at an example of a user class.

class vec {
    int x = 0;
    int y = 0;
    int z = 0;
    vec(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
    method vector toVector() {
        return new vector(x, y, z);
    }
    method vec add(vec a) {
        return new vec(x + a.x, y + a.y, z + a.z);
    }
    method vec sub(vec a) {
        return new vec(x - a.x, y - a.y, z - a.z);
    }
    tostring() {
        return "vec(" + x + "," + y + "," + z + ")";
    }
}
Class Statement

We start with the class statement, which defines the classes name in our example it is 'vec'.

class className {
}
attributes

Next your notice we define some variables inside the classes main block of statements, variables defined here are actually known as attributes. These attributes will always default to the value they are defined, however each instance of a class is separate to that of other instances of the same class.

class myClass{
    int var = value;
}
Constructor

Our next statement is the constructor this is whats called when we need a new instance of that class e.g

new vec(1,2,3);

A constructor statement is always the name of the current class and then its parameters. A constructor will always contain the a variable called 'this' which is the new instance of the class and is the return value of the constructor.

You will also notice that here that attributes on a class can be accessed directly of the instance of the class.

int example = classObj.attribute

classObj.attribute = example;

This will also work outside of the class statement.

Methods

User classes use methods for functionality, A method is almost the same as a user function however just like a constructor it also has a variable called 'this' which is the instance of the class.

method returnType methodName() { //This is a method. }

Operators

E3 currently only supports 1 operator for user class as all other operations can be replicated using methods.

The tostring operator must return a string and is used when ever trying to concatenate your object or running a string conversion. This means that instead of creating an operator for addition or subtraction you would create a method to this this for you. If you look at the provided example, if we wanted to add two different vec objects together we would have to do

vec a = new vec(1,2,3);
vec b = new vec(4,5,6);
vec total = a.add(b);