The Berry Script Language.

What is it ?

Berry is a ultra-lightweight dynamically typed embedded scripting language. It is designed for lower-performance embedded devices. The Berry interpreter-core's code size is less than 40KiB and can run on less than 4KiB heap (on ARM Cortex M4 CPU, Thumb ISA and ARMCC compiler).

The interpreter of Berry include a one-pass compiler and register-based VM, all the code is written in ANSI C99. In Berry not every type is a class object. Some simple value types, such as int, real, boolean and string are not class object, but list, map and range are class object. This is a consideration about performance. Register-based VM is the same meaning as above.


Berry has the following advantages

Lightweight

A well-optimized interpreter with very little resources. Ideal for use in microprocessors.

Fast

Optimized one-pass bytecode compiler and register-based virtual machine.

Powerful

Supports imperative programming, object-oriented programming, functional programming.

Flexible

Berry is a dynamic type script, and it's intended for embedding in applications. It can provide good dynamic scalability for the host system.

Simple

Simple and natural syntax, support garbage collection, and easy to use FFI (foreign function interface).

RAM saving

With compile-time object construction, most of the constant objects are stored in read-only code data segments, so the RAM usage of the interpreter is very low when it starts.


Features

  • Base Type
    • Nil: nil
    • Boolean: true and false
    • Numerical: Integer (int) and Real (real)
    • String: Single quotation-mark string and double quotation-mark string
    • Class: Instance template, read only
    • Instance: Object constructed by class
    • Module: Read-write key-value pair table
    • List: Ordered container, like [1, 2, 3]
    • Map: Hash Map container, like { 'a': 1, 2: 3, 'map': {} }
    • Range: include a lower and a upper integer value, like 0..5
  • Operator and Expression
    • Assign operator: =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
    • Relational operator: <, <=, ==, !=, >, >=
    • Logic operator: &&, ||, !
    • Arithmetic operator: +, -, *, /, %
    • Bitwise operator: &, |, ~, ^, <<, >>
    • Field operator: .
    • Subscript operator: []
    • Connect string operator: +
    • Conditional operator: condition ? val_true : val_false
    • Brackets: ()
    • Bytes buffer support
  • Control Structure
    • Conditional statement: if elif else end
    • Iteration statement: while, for
    • Jump statement: break, continue
  • Function
    • Local variable and block scope
    • Return statement
    • Nested functions definition
    • Closure based on Upvalue
    • Anonymous function
    • Lambda expression
  • Class
    • Inheritance (only public single inheritance)
    • Method and Operator Overload
    • Constructor method
    • Destructive method
  • Module Management
    • Built-in module that takes almost no RAM
    • Extension module support: script module, bytecode file module and shared library (like *.so, *.dll) module
    • Ability to solidify code, classes and modules in flash to reduce RAM usage
    • Optional Regex support
    • Optional LVGL mapping
  • GC (Garbage collection)
    • Mark-Sweep GC
  • Exceptional Handling
    • Throw any exception value using the raise statement
    • Multiple catch mode
  • Bytecode file support
    • Export function to bytecode file
    • Load the bytecode file and execute
  • Native C interface
    • Can be easily embedded as a library in existing code like Tasmota
    • Optional easy mapping to call C code from Berry

Brief tour

Simple and intuitive syntax

The Berry language syntax is similar to that of high-level languages and will be familiar to most programmers.

var list = [ "Hello", "World" ]
for text : list
    print(text)
end

Map Instance

The map class is a built-in class type that is used to provide an unordered container of key-value pairs. Within the Berry interpreter, map map uses the Hash table to implement. You can use key-value pairs to construct a map container.

var map = {
    'greets': def() return 'Hi! Berry' end (),
    'say'   : 'Hello World',
    'sum'   : 10 + 50
}
for i : map.keys()
    print(i, "\t=>", map[i])
end

Object Orientation

To use a class, you must first declare it. The declaration of a class begins with the keyword class.

The member variables and methods of the class must be specified in the declaration. This is an example of class declaration:

import string

class Parent
    #- member variables  -#
    var fname
    var cname

    #- constructor -#
    def init()
        self.fname = 'Darth vader'
    end

    #- Method -#
    def say()
        return string.format(
            '%s say\n%s %s', self.fname,
            self.cname, "i'm your father"
        )
    end
end

Class inheritance

Berry only supports single inheritance, that is, a class can only have one base class, and the base class uses the operator : to declare :

Here the Child class inherits from the Parent class. The subclass will inherit all the methods and properties of the Parent class, and you can override them in the subclass. This mechanism is called Overload.

class Child : Parent
    #- constructor -#
    def init(name)
        self.fname = Parent().fname
        self.cname = name
    end
end

child = Child('Luke')
print( child.say() ) #child inherits from parent the say method

Examples

You can also check out these Berry example source files on GitHub