Attila's Blog

About writing your own programming language in C, Go and Swift

The ASPL language - part 3

Jul 26, 2018 Comments

Categories: aspl
Tags: aspl

Let’s continue the overview of ASPL with statements, blocks, variables, control flow, functions, and closures.

Statements

In the previous article we saw that expressions return a value. Statements on the other hand won’t return values. They carry out some action and their main job is to produce a side effect. This usually means modifying some state, reading input or writing output.

For example

print "Hello world";

is a statement that evaluates the string “Hello world” and displays the result on the screen.

There are also statements that consists of an expression and a semicolon (;):

12 * 3; // evaluates to 36 but the result gets discarded
"Hello again";  // evaluates to 'Hello again' but the result gets discarded

They will cause the associated expression to be evaluated. They are called expression statements.

Blocks

Blocks are structures that group multiple statements into a single one. If there is a place in the code where a single statement is expected you can provide a block containing several statements. In ASPL blocks are wrapped in braces { ... }.

{
    print "Hello world";
    print 126;
}

Variables

ASPL has both global and local variables. Variable declaration looks like this:

var x = 12;
var y = "apple";

Because we have dynamic typing you don’t have to specify the type of the variable, it will be automatically inferred, based on the type of the expression on the right hand side. It is also possible to omit the initialization.

var z;

In this case the variable will contain nil.

Once variables are declared their value can be accessed via their name. You can also assign a new value to the variable by its name.

var greeting = "Good morning";
print greeting; // Good morning

greeting = "Good afternoon";
print greeting; // Good afternoon

Control flow

During programming, sooner or later you will need to execute different code paths based on some condition, and also to repeat the same code path multiple times based on a condition. These are called control flow.

Control flow is done via special statements in ASPL, and there are three of them.

The if () statement executes one of its two statements based on a condition.

if ("apples" == "oranges") {
    print "Apples and oranges are the same!";
} else {
    print "Apples and oranges are totally different things!";
}

The while statement executes its body as long as the condition is true.

var x = 0
while (x < 10) {
    print x++;
}

And the last supported control flow statement if the for statement.

for (var x = 0; x < 10; x++) {
    print x;
}

Both for and while statements are basically the same thing, they just look different.

Functions

ASPL has both global and local functions. That means you can define functions in the global scope, and also in local scopes (inside another function, for example). Function definition is done via the func keyword. If there are any parameters to the function, you have to provide them inside parentheses. You can also define functions without parameters, in this case the parentheses are empty. The body of a function is a block.

func sayHello(name) {
    print "Hello " + name + "!";
}

func sayHelloToJoe() {
    print "Hello Joe!";
}

Function calls look like this:

sayHello("Bob"); // Hello Bob!
sayHelloToJoe(); // Hello Joe!

If you want to return some value from the function you use the return keyword. Functions that does not have a return statement will implicitly return nil.

func sum(x, y) {
    return x + y;
}

Closures

In ASPL functions are first-class, which means they are real values that you can store, retrieve and pass around as parameters to other functions.

So this will work just fine:

func addition(x, y) {
    return x + y;
}

func subtraction(x, y) {
    return x - y;
}

func arithmeticOperation(operation) {
    return operation;
}

var sub = subtraction;

arithmeticOperation(addition)(2, 1); // 3
arithmeticOperation(sub)(2, 1); // 1

I also said that ASPL has local functions, that means you can define a function in another, for example:

func outerFunction() {
    func innerFunction(x) {
        print x;
    }
    
    return innerFunction(2);
}

print outerFunction(); // 2

What happens if we combine these two principles?

func outerFunction() {
    var name = "Bob";
    
    func innerFunction() {
        print "Hello " + name + "!";
    }
    
    return innerFunction;
}

var greeter = outerFunction();
greeter(); // Hello Bob!

Ok, what just happened here?

We defined a function, and another inside it. The inner function accesses a variable declared in the surrounding function. The outer function then returns a reference to the inner function. When we call the outer function and then greeter it will correctly print Hello Bob! which is the expected result.

For this to work, the inner function has to hold on to the references to any variables that was defined in one of the surrounding scopes, to make sure it lives even after the function that originally defined that variable has returned.

We call functions like innerFunction closures, because it “closes over” variables that are defined outside of its own scope.

Next time I will finish the overview with the introduction of classes, inheritance, and the standard library.

Stay tuned. I’ll be back.

Tags: aspl

← The ASPL language - part 2 The ASPL language - part 4 →

comments powered by Disqus