What's new in TB 3.0

TB 3.0.0 brings classes, pattern matching, generators, tasks and a much bigger standard library to the same friendly language, plus a formatter, a checker, a debugger, packages and a VS Code extension. Press Run under any example to try it here.

Version 3.0.0Released DownloadMoving from 2.x

The language

Everything below is explained step by step in the TB 3.0 lessons, and exactly in the language specification.

Classes, enums and pattern matching

class with fields, methods, static members, extends and super; special methods such as text(), equals() and compare(); enum; and match with value, range, list, map, type and class patterns, or and guards.

enum Level { low, medium, high }

class Alert {
  fn init(rule, level) {
    self.rule = rule
    self.level = level
  }
}

let a = Alert("ssh brute force", Level.high)
say(match a {
  Alert {level: Level.high} => f"page someone: {a.rule}"
  _ => "log it"
})

Functions and data

Default, named, ...rest and **opts arguments; lambdas (x => x * 2); destructuring in let, for and parameters; spread ([...a, ...b], {...m}, f(...xs)); swaps (a, b = b, a); const; and if ... then ... else as an expression.

fn scan(host, port = 22, timeout = 5) {
  return f"{host}:{port} ({timeout}s)"
}
let {name, ip, owner = "nobody"} = {"name": "web01", "ip": "192.0.2.10"}
say(scan(ip, timeout = 1), name, owner)
say([3, 1, 2].map(x => x * 10), if len(name) > 3 then "long" else "short")

Numbers, text and bytes

Whole numbers and fractions are separate (//, **, is_int()); hex, binary and octal literals with _; bit operators; chained comparisons; format specs in f-strings ({x:.2f}, {n:,}, {n:08b}, {p:.1%}, {s:>10}); escapes, raw r"..." and multi-line """...""" text; a bytes type with b"..."; and ranges (1..5, 1..=5) and slices (xs[1:3], xs[::-1]).

say(0xff, 0b1010, 1_000_000, 7 // 2, 2 ** 10, 0b1100 & 0b1010)
say(f"{1234.5678:,.2f} | {255:x} | {0.257:.1%} | {"tb":>6}")
let data = b"MZ\x90\x00"
say(data.hex(), data[0], (1..=5).to_list(), "detection"[::-1])

Errors, generators and tasks

Errors are objects with a kind: catch err: ParseError, your own classes that extend Error, and finally. Generators (yield) make lazy pipelines with map, filter, take and zip. async fn starts a task and await collects its answer. defer and assert round it off.

import json

fn evens() {
  let n = 0
  while true {
    yield n
    n += 2
  }
}
say(evens().filter(n => n % 3 == 0).take(4).to_list())

try {
  json.parse("{oops")
} catch err: ParseError {
  say("kind:", err.kind)
} finally {
  say("done")
}

Modules and packages

export decides what a module shares, from ... import picks names, and packages install into a project with tb add. See Packages.

A bytecode virtual machine

Programs are now compiled to bytecode and run on a virtual machine, which is faster than the 2.x interpreter on loops, function calls and methods, and runs generators without threads. Error messages are the same as before. The 2.x-style tree-walking interpreter is still there for one release: TB_ENGINE=tree tb file.tb or tb --engine tree file.tb.

The playground on this site runs the same virtual machine in your browser.

Concurrency

async fn and await run work as tasks on their own threads, and the parallel library adds parallel.map, parallel.run, parallel.all, queues and locks. Desktop windows stay responsive with win.run_in_background.

import net
import parallel

let hosts = ["192.0.2.10", "192.0.2.11", "192.0.2.12"]
let open = parallel.map(h => net.tcp_check(h, 443, 2), hosts, 16)
say(hosts.zip(open))

The browser playground has no threads: tasks there run one after another, and parallel needs the TB app.

Tools

New commands for bigger projects, all part of the tb program: tb fmt (formatter), tb check (finds mistakes before you run, with optional type hints), tb debug (breakpoints and stepping), tb test --coverage, tb doc for your own files, tb build (a folder you can give to someone without TB), tb lsp (a language server for editors), and tb init, tb add, tb remove, tb update and tb packages for packages. The VS Code extension brings them into your editor.

See every tool with examples.

New libraries

The standard library grew from 20 to 43 libraries. New in 3.0:

Coredatetime decimal binary
Files and datayaml toml xml sqlite archive template
Security operationscrypto pcap evtx pe elf scan attack stix siem
Networkingdns tls websocket server
Concurrencyparallel

Existing libraries grew too: detect runs full Sigma (all modifiers, aggregations, correlation rules) and converts rules to Splunk, Elastic and Sentinel queries; logs reads EVTX files and keeps time zones; fs reads and writes bytes and watches folders; net sends UDP; gui has tabs, trees, charts, canvases, sliders, themes and notifications; test has mocks, fixtures, parametrised cases and coverage.

import pcap

let packets = pcap.read("apps/data/sample.pcap")
for q in pcap.dns_queries(packets).slice(0, 3) {
  say(q.name, q.answers)
}

New apps

The apps gallery now has every app that ships with TB, including pcap-triage, evtx-triage, file-inspector (and a desktop version), sigma-studio, ioc-feed, siem-forwarder, alerts-dashboard-gui, network-monitor, parallel-scan, dns-check, cert-check and dashboard, a web dashboard served by the server library.

What might break

Almost every 2.x program runs unchanged and prints the same. The few changes: new reserved words (class, match, from, is, const, ... can't be names any more, and TB suggests another), / always gives a fraction (displayed as before), and errors from libraries have specific kinds. The migration guide lists everything with fixes.

Earlier releases

2.0.1

Neutral sample programs and docs; no language changes. The first program became welcome.tb, a short tour of TB.

2.0

Maps, methods, f-strings, modules and import, try/catch/throw, twenty libraries (desktop GUI, logs, detection rules, IOCs, hashing, networking, threat intel), the Windows installer and the security apps.