Skip to content

Reference

chibash edited this page Jan 8, 2025 · 21 revisions

BlueScript Reference

Introduction

BlueScript is a scripting language for programming a microcontroller. It borrows its syntax from TypeScript and hence it is regarded as a small subset of TypeScript. But BlueScript also adopts several unique semantic differences from TypeScript.

BlueScript is a subset of TypeScript because, for example, it (currently) does not support exceptions, promises, async/await, or certain built-in objects. On the other hand, unlike TypeScript, BlueScript supports integer and float primitive types, and a BlueScript program is executed relying on static type information. When a variable is statically typed as integer, it always holds a native 32bit integer during runtime as the C/C++ language does. It never holds another type of value, and thus no runtime type check is necessary. Furthermore, BlueScript supports simple gradual typing.

Primitive Types

BlueScript provides six primitive types as well as object types:

  • integer (32bit integer)
  • number (an alias of integer)
  • float (32bit floating-point number)
  • string
  • boolean
  • null and undefined (they are the same)
  • any

Any kind of value can be implicitly converted into any type, and vice versa.

  • When an integer value is converted, the resulting value is represented as a 30bit integer.
  • When a float value is converted, the resulting value is represented as a 30bit floating-point number, where only 6 bits are allocated for an exponent instead of 8 bits.
  • For logical operations and the condition expressions of coditional/loop statements such as if and while, 0, 0.0, false, null, and undefined are considered as false while other values are true.

Literals

  • null and undefined (they are the same)
  • true
  • false
  • Numbers such as 7 and 0.3
  • Character strings such as "text" and 'text'
  • Arrays such as [1, 2, 3]

Note that object literals such as { x: 3, y: 4 } are not supported. An object must be created by the new operator as an instance of a class.

Built-in Objects

String

An object represents a character string.

Array

Array Objects

BlueScript currently supports arrays of integer, float, boolean, string, class types, array types, and any-type. Their names are T[], where T is an element type.

Array types are invariant. For example, integer[] is not a subtype of any[] or its super type. But array types can be implicitly converted into any-type, and vice versa. In other words, a reference to an array of integer, any, etc. is implicitly converted into an any-type value. An any-type value is also implicitly converted into a reference to an array if the any-type value points to an array object of that array type. Otherwise, a runtime error is thrown.

Array Literals

Array literals are defined using square brackets, with elements separated by commas.

let arr = [1, "Foo", 42];

The type of an array literal is array of the most specifict super type of the static types of all the elements. If there exists such a super type, the type of the array literal is array of any.

Array Construction

An array object is created by new Array<T>(size, value). Here, T is a meta variable representing a type name. size is the number of the array elements. value is the initial value for the array elements. T can be integer, float, boolean, string, an array type, a class type, or any-type.

let iarr = new Array<integer>(3, 0);

When the element type is integer, float, boolean, or any, the second argument to the Array constructor can be omitted. The initial values are zero, false, or undefined. For example, new Array<integer>(7) is a valid expression, and it constructs an array including 7 elements.

Type Annotations

Array types are represented by Type[]. Here, Type is a meta variable representing the type name of array elements.

let iarr: integer[] = [1, 2, 3];
let sarr: string[] = ['one', 'two', 'three'];

Note that Array or Array<integer> is not a valid type name.

Accessing Elements

Only numeric indices are supported for accessing array elements.

let arr = [1, 3, 4];
print(arr[0]); // 1

Accessing an index out of bounds will result in a runtime error.

let arr = [1, 2, 3];
print(arr[5]); // ** error: array index out of range: 5

Currently, array methods such as push, pop, map, filter, etc., are not supported in BlueScript.

The length property represents the length of an array.

let arr = [1, 2, 3];
print(arr.length);    // 3

If the type of arr is any-type, the type of arr[i] is any. arr[i] = v throws a runtime error if v is not a value of the element type for the array that arr points to. For example,

let arr: integer[] = [1, 2, 3];
let arr2: any = arr;
print(arr2.length)        // 3
print(arr2[0])            // 1
print(typeof arr2[0]);    // 'any'
arr2[1] = 'five';         // runtime error

Byte arrays

An Uint8Array object is also available. Its elements are unsigned 8 bit integers.

let arr = new Uint8Array(3, 0)    // create an array containing 3 elements.  Their initial values are 0.
print(arr[1])      // 0
arr[0] = 7
print(arr[0])      // 7
print(arr.length)  // 3

Then second argumemnt to the constructor of `Uint8Array' cannot be omitted.

Expressions and Operators

Operators

Increment and Decrement Operators

The operands' types of the increment and decrement operators must be integer, float, or any.

  • Postfix Increment/Decrement Operators
    The value is incremented or decremented by 1, and the original value is returned.

    let x = 1;
    print(x++); // 1
    print(x);   // 2
    
    let y = 1.0;
    print(y--); // 1.0
    print(y);   // 0.0
  • Prefix Increment/Decrement Operators
    The value is incremented or decremented by 1, and the updated value is returned.

    let x = 1;
    print(++x); // 2
    print(x);   // 2

Unary Operators

BlueScript supports the following unary operators:

  • + (Unary plus, returns the value of its operand. The operand's type must be integer, float, or any.)
  • - (Unary negation, returns the negation of its operand. The operand's type must be integer, float, or any.)
  • ~ (Bitwise NOT. The operand's type must be integer or any.)
  • ! (Logical NOT. The operand's type can be any kind of type.)
print(-3)  // -3

Arithmetic Operators

The operands' types must be integer, float, or any. If either the left operand or the right operand is any, the resulting type is any. If either left or right is float, the resulting type is float. Otherwise, it is integer.

  • + (Addition)
  • - (Subtraction)
  • * (Multiplication)
  • / (Division)
  • % (Modulus) The operands must be an integer value or an any-type value holding an integer value.
  • ** (Exponentiation) The operands are converted into double values, and pow() in C computes the result.

Relational Operators

The operands' types must be integer, float, string, or any. The resulting type is boolean. Both operands share the same type or any-type. If a left or right operand is any-type, the value of the other operand is converted into an any-type value before comparison.

  • < (Less than)
  • > (Greater than)
  • <= (Less than or equal to)
  • >= (Greater than or equal to)

Equality Operators

  • == and === are used as equality operators. Their semantics is the same as the === operator in JavaScript.
  • != and !== are used as inequality operators. Their semantics is the same as the !== operator in JavaScript.

If either the left operand or the right operand is a boolean type, the other operand must be also boolean type.

Bitwise Shift Operators

The operands' types must be integer. The type of the resulting value is integer.

  • << (Left shift)
  • >> (Right shift)
  • >>> (Unsigned right shift)

Binary Bitwise Operators

The operands' types must be integer. The type of the resulting value is integer.

  • & (AND)
  • | (OR)
  • ^ (XOR)

Binary Logical Operators

  • && (Logical AND)
  • || (Logical OR)

Type operators

  • typeof (returns the static type name of its operand. Unlike JavaScript, it is not a dynamic type name.)

  • instanceof (checks whether the left operand is an instance of the class given as the right operand or its subclass. The right operand may be string or Array. The type of the left operand must be a class type, string, an array type, or any.)

obj instanceof Array results in true when obj is an array object no matter what its element type is.

Ternary Operator

The ternary operator ? : is used for conditional expressions:

let result = condition ? trueValue : falseValue;

Assignment Operators

BlueScript supports the following assignment operators:

  • =
  • +=, -=, *=, /= (Compound assignment operators. The operands' type must be integer, float, or any.)
  • %= (The operands' type must be integer or any.)

Statements and Declarations

Variable Declarations

Variables in BlueScript are declared using let or const.

let x = 10;
let y: float = 5.5;
const t: boolean = true

Type annotations are optional. When they are ommitted, the variables' types are determined by type inference.

Loops

  • while loop

    let i = 0;
    while (i < 10) {
        print(i);
        i++;
    }
  • do-while loop

  let i = 0;
  do {
      print(i);
      i++;
  } while (i < 10);
  • for loop

    for (let i = 0; i < 10; i++) {
        print(i);
    }
  • break and continue

    for (let i = 0; i < 10; i++) {
        if (i == 5) {
            continue;
        }
        if (i == 8) {
            break;
        }
        print(i);
    }

Conditional Statements

  • if … else …

    let a = 10;
    if (a > 5) {
        print("Greater than 5");
    } else {
        print("Less than or equal to 5");
    }

Functions

Declarations

Functions are declared with the function keyword. Function declarations must be at the top level. They must not be declared inside a function body or an expression.

function add(a: integer, b: integer): integer {
  return a + b;
}

A function parameter without a type annotation is typed as any. A return type is optional. When it is omitted, it is determined by type inference. If type inference fails, the return type is either any or void.

A function can be redefined, but a new definition must share the same parameter types and return type.

Arrow Functions

An arrow function is also available. It can be created not only at the top level but also within a function body.

let add = (a: integer, b: integer): integer => a + b;

An arrow function creates a closure.

Classes

An object is created as an instance of class. The type of the object is its class name. An object can be implicitly converted into any type, and vice versa.

Class declarations

A class must be defined by a class declaration. A class expression is not supported. Classes are declared by using class keyword.

class Rectangle {
  height: float;
  width: float;

  constructor(height: float, width: float) {
    this.height = height;
    this.width = width;
  }

  getArea() {
    return this.height * this.width;
  }
}

A property may have a type annotation, but a type annotation is optional. In the code above, the types of properties height and width are float.

All class declarations, properties, and methods are public. No access modifiers such as private and readonly are (currently) not supported.

extends

A class can inherit from another class. extends keyword is used to specify a super class.

class Square extends Rectangle {
  constructor(sideLength: float) {
    super(sideLenght, sideLength);
  }
}

A constructor must first invokes the constructor of its super class. A subclass is a subtype of the type representing its super class.

new

new keyword is used to create a class instance.

let rect = new Rectangle(13, 15);

Properties and methods

Properties and methods are accessed using the dot notation.

print(rect.height);    // 13
print(rect.getArea()); // 195

Unlike TypeScript, a method is not a property holding a function object. It may not be accessed as a property. For example, since getArea is a method, the following code is not valid in BlueScript.

let m = rect.getArea;     // error

The rect objecrt does not have a property named getArea.

When a property's type is integer or float, an object may hold only a 30bit value as the property's value. If a class has a property of object type, an integer or float property of its subclass holds a 30bit value. Otherwise, it holds a 32bit value.

Constructors

A property must be explicitly initailized in a constructor. A property declaration (currently) must not have an initializer, which is an expression computing the initial value of that property.

class Rectangle {
  height: float = 10.0;    // error
  width: float = 10.0;     // error
}

This is valid in TypeScript, but it is not in BlueScript. = 10.0 must be erased.

In a constructor, a property is initialized through this. this is a special variable that is available only in method bodies and a constructor. It refers to their object. Unlike TypeScript, this is not available in a function body or at the top level.

Nullable type

A class type is not a super type or a subtype of null type. So, a variable of a class type may not be set to null. Only a variable of a nullable type may be set to null.

A nullable type is constructed from a class type. Suppose that T is a class type name. Then, T | null and T | undefined are nullable types (Note that null and undefiend are identical in BlueScript). A variable of T | null may be set to either a T object or null (or undefined).

let r: Rectangle | null = null
r = new Rectangle(3.0, 4.0)

A nullable type is also constructed from the string type and array types. All the following types are valid nullable types.

string | null
integer[] | null
string[] | null
Uint8Array | null

However, integer | null or float | null is not a valid type.

Subtyping

If type T is a subtype of S, then T | null is also a subtype of S | null.

Type guards

BlueScript (currently) supports only a very simple type guard. If a local varialbe of type T | null is tested for null in the condition expression of a if statement, that variable is treated as a variable of type T in the then clause or the else clause.

function area(r: Rectangle | null): float {
  if (r == null)
    return 0.0;
  else {
    // the static type of r is Rectangle.  Not Rectangle | null.
    return r.getArea();
  }
}