.doc_for_ai/BERRY_LANGUAGE_REFERENCE.md
Compact reference for Generative AI. For Tasmota-specific features, see BERRY_TASMOTA.md.
# Line comment
#- Block multiline comment -#
40 0x80 3.14 1.1e-6 true false nil
'string' "string" "a" "b" # adjacent strings concatenate
Escapes: \n \r \t \\ \" \' \0 \xhh \uXXXX
if elif else while for def end class break continue return
true false nil var do import as try except raise static
Simple: nil, int, real, bool, string, function, class, instance, module, comptr Built-in classes: list, map, range, bytes
Note: type() returns the base type, not the class name. For list/map/range/bytes instances, type() returns 'instance'. Use classname(x) to get 'list', 'map', etc.
a = 1 # global if no var declaration in scope
var a # nil
var a = 1
var a, b = 1, 2
Outermost block = global; inner blocks = local. do ... end creates a scope.
Arithmetic: + - * / % (unary -)
Relational: < <= == != >= >
Logical: && || ! (short-circuit)
Bitwise: ~ & | ^ << >>
Assignment: = += -= *= /= %= &= |= ^= <<= >>=
Walrus: := (assign + return value in expressions)
Other: . [] ?: ..
.. = range (int..int), string concat (auto-converts RHS), or list/bytes append-in-place.
"ab" * 3 → "ababab". Accepts int or bool. "s" * false → "".
if cond ... elif cond2 ... else ... end
while cond ... end
for i: 0..5 ... end # inclusive range
for i: range(0, 10, 2) ... end # stepped
for item: [1,2,3] ... end # container
for val: mymap ... end # map values
for key: mymap.keys() ... end # map keys
for i: 10.. ... end # open-ended (upper = MAXINT)
do ... end # scoped block
break continue
import math
import hardware as hw
def add(a, b) return a + b end
# Anonymous
f = def (a, b) return a + b end
# Lambda (single expression, commas optional between params)
f = / a b -> a + b
f = /-> nil # zero-arg
# Varargs
def f(a, *args) end # args is a list
# Dynamic call (last arg list is unpacked)
call(func, 1, [2, 3]) # func(1, 2, 3)
Closures capture variables by reference from enclosing scope.
class Person
var name, age
def init(name, age) self.name = name self.age = age end
def greet() print("Hello, I'm", self.name) end
end
Static members:
class C
static var PI = 3.14 # preferred
static PI2 = 6.28 # also valid
static def square(x) return x * x end
static class Inner ... end # nested class
end
Instance methods access statics via self. Static methods get implicit _class.
Static methods can be called on the class (C.square(5)) or on an instance (c.square(5)), but they do not receive self.
Inheritance: class B : A ... end
super(self).init(args) # auto-infers parent from calling context
super(self, TargetClass) # explicit ancestor
super(Class) # parent class of a class
Operator overloading — define methods named after the operator:
Overloadable: + - * / % < <= == != > >= & | ^ << >> .. ~ ()
def +(other) ... end # binary
def -() ... end # unary neg
def ~() ... end # unary flip
def ()() ... end # call operator
def tostring() ... end # string conversion
def tobool() ... end # boolean conversion
def toint() ... end # int() conversion
Virtual member dispatch: define member(name) / setmember(name, value) on a class or module to handle undefined attributes dynamically. Return import undefined from member() to signal attribute doesn't exist.
Indirect member access: obj.('name') — dynamic member name from expression.
for calls iter() on the object, which returns a closure. The closure yields values and raises 'stop_iteration' when done.
l = [1, 2, 3]
l[0] l[-1] l[1..3] l[1..] l[0..-2]
l.push(x) l.pop() l.pop(i) l.insert(i, x) l.remove(i)
l.resize(n) l.clear() l.size() l.reverse() l.copy()
l.find(x) # index or nil
l.concat() l.concat("-")
l + [4,5] # new list (does not mutate l)
l .. x # append in place, returns l
l.keys() # range of indices
[1,2] == [1,2] # deep comparison
m = {"a": 1, 0: true}
m["a"] # raises key_error if missing
m.find("a") # nil if missing
m.find("a", default)
m["k"] = v
m.insert("k", v) # true if new, false if exists
m.remove("k") m.size() m.contains("k")
0..5 # inclusive, increment 1
10.. # open-ended
range(1, 10) # explicit
range(1, 10, 2) # custom increment
range(30, 0, -3) # negative increment
r.lower() r.upper() r.incr() r.setrange(lo, hi) r.setrange(lo, hi, step)
s[0] s[-1] s[1..3] s[1..] s[0..-2] # slicing, tolerant of out-of-range
Strings have no methods. Use import string for string operations (e.g. string.find(s, sub), not s.find(sub)).
b = bytes() bytes("1122AA") bytes(10) bytes(-8) # -N = fixed size
b[0] b[1..2] b[0] = 0xFF
b.size() b.resize(n) b.clear() b.reverse() b.copy()
# Structured I/O (size: 1,2,-2,4,-4; negative=big-endian)
b.get(off, sz) b.geti(off, sz) b.set(off, val, sz) b.add(val, sz)
b.getfloat(off) b.setfloat(off, v) b.addfloat(v)
b.setbytes(off, other)
b.tohex() b.asstring() b.tob64()
b.fromhex(s) b.fromstring(s) b.fromb64(s)
b1 .. b2 # append in place
b1 == b2 # content equality
f = open("file.txt", "r")
f.read() f.readline() f.readbytes() f.close()
f = open("file.txt", "w")
f.write(s) f.flush() f.close()
f.seek(pos) f.tell() f.size()
print(...) input("prompt")
type(x) classname(x) classof(x)
isinstance(inst, cls) issubclass(cls, parent)
int(x) real(x) bool(x) str(x) number(s) size(x)
compile("code") compile("file.be", "file")
super(self) super(self, Class) super(Class)
assert(cond) assert(cond, "msg")
format(fmt, ...) call(func, args...)
module() module("name")
import string
string.find(s, sub [, start [, end]]) # -1 if not found
string.count(s, sub [, start [, end]])
string.split(s, sep [, max_splits])
string.byte("A") string.char(65)
string.toupper(s) string.tolower(s)
string.tr(s, from, to) string.replace(s, old, new)
string.startswith(s, prefix [, case_insensitive])
string.endswith(s, suffix [, case_insensitive])
string.escape(s [, single_quote])
string.format(fmt, ...) # %d %i %u %o %x %X %f %e %E %g %G %s %c %q %%
f"Value: {x}" f"{x:.2f}" f"{x=}" f"{{literal braces}}"
import math
math.pi math.inf math.nan math.imin math.imax
math.abs(x) math.floor(x) math.ceil(x) math.round(x)
math.min(...) math.max(...)
math.sqrt(x) math.pow(x,y) math.exp(x) math.log(x) math.log10(x)
math.sin(x) math.cos(x) math.tan(x)
math.asin(x) math.acos(x) math.atan(x) math.atan2(y,x)
math.deg(rad) math.rad(deg)
math.srand(seed) math.rand()
math.isinf(x) math.isnan(x)
import json
json.load('{"a":1}') # parse, nil on error
json.dump(obj) # compact
json.dump(obj, "format") # pretty
import re
re.search(pat, str) # ['match', 'group1', ...] or nil
re.match(pat, str) # anchored to start
re.match2(pat, str) # [match_len, 'group1', ...]
re.searchall(pat, str) # list of all matches
re.matchall(pat, str) # consecutive matches from start
re.split(pat, str)
# Pre-compiled:
p = re.compile(pat)
p.search(s) p.match(s) p.match2(s) p.searchall(s) p.matchall(s) p.split(s)
import os
os.getcwd() os.chdir(p) os.mkdir(p) os.remove(p)
os.listdir() os.listdir(p) os.system(cmd) os.exit()
os.path.exists(p) os.path.isfile(p) os.path.isdir(p)
os.path.split(p) os.path.splitext(p) os.path.join(a, b)
import global
global() # list all global names
global.contains("name")
global.var_name # get (nil if absent)
global.var_name = 42 # set
global.("dynamic") # indirect access
global.undef("name") # remove
import introspect
introspect.members(obj) # list member names
introspect.get(obj, "attr") # nil if missing (protected)
introspect.set(obj, "attr", v)
introspect.contains(obj, "a")
introspect.name(obj) # name of class/closure/module
introspect.ismethod(fn)
introspect.module("math") # dynamic import, no global created
introspect.setmodule("n", m) # override cached module
introspect.toptr(obj) # to comptr
introspect.fromptr(ptr) # comptr back to object
introspect.solidified(obj) # is constant/ROM?
Exceptions: assert_failed attribute_error index_error io_error key_error runtime_error stop_iteration syntax_error type_error unrealized_error value_error
raise "error_type", "message"
try ... except "type" as e, msg ... end
try ... except "t1", "t2" as e, msg ... end
try ... except .. as e, msg ... end # catch all
Many functions return nil on error instead of raising (json.load, map.find, list.find).
Directives must start at beginning of line (# mid-line = comment):
#define NAME 1 # truthy
#define NAME 0 # falsy
#define NAME # truthy (defined, no value)
#undef NAME
#if NAME ... #elif NAME2 ... #else ... #endif
#if !UNDEFINED ... #endif # negation
Nesting up to 8 levels. #define inside skipped blocks is ignored.
Runtime: import preproc → preproc.define('X','1') preproc.undef('X') preproc.clear()