11 KiB
title, description, weight, type
| title | description | weight | type |
|---|---|---|---|
| ƿit Language | Syntax, types, operators, and built-in functions | 10 | docs |
ƿit is a scripting language for actor-based programming. It combines a familiar syntax with a prototype-based object system and strict immutability semantics.
Basics
Variables and Constants
Variables are declared with var, constants with def. All declarations must be initialized and must appear at the function body level — not inside if, while, for, or bare {} blocks.
var x = 10
var name = "pit"
var empty = null
def PI = 3.14159 // constant, cannot be reassigned
var a = 1, b = 2, c = 3 // multiple declarations
Data Types
ƿit has eight fundamental types:
- number — DEC64 decimal floating point (no rounding errors)
- text — Unicode strings
- logical —
trueorfalse - null — the absence of a value (no
undefined) - array — ordered, numerically-indexed sequences
- object — key-value records with prototype inheritance
- blob — binary data (bits, not bytes)
- function — first-class callable values
Literals
// Numbers
42
3.14
-5
0
1e3 // scientific notation (1000)
// Text
"hello"
`template ${x}` // string interpolation
`${1 + 2}` // expression interpolation
// Logical
true
false
// Null
null
// Arrays
[1, 2, 3]
[]
// Objects
{a: 1, b: "two"}
{}
// Regex
/\d+/
/hello/i // with flags
Operators
Arithmetic
2 + 3 // 5
5 - 3 // 2
3 * 4 // 12
12 / 4 // 3
10 % 3 // 1
2 ** 3 // 8 (exponentiation)
Comparison
All comparisons are strict — there is no type coercion.
5 == 5 // true
5 != 6 // true
3 < 5 // true
5 > 3 // true
3 <= 3 // true
5 >= 5 // true
Logical
true && true // true
true && false // false
false || true // true
false || false // false
!true // false
!false // true
Logical operators short-circuit:
var called = false
var fn = function() { called = true; return true }
var r = false && fn() // fn() not called
r = true || fn() // fn() not called
Bitwise
5 & 3 // 1 (AND)
5 | 3 // 7 (OR)
5 ^ 3 // 6 (XOR)
~0 // -1 (NOT)
1 << 3 // 8 (left shift)
8 >> 3 // 1 (right shift)
-1 >>> 1 // 2147483647 (unsigned right shift)
Unary
+5 // 5
-5 // -5
-(-5) // 5
Increment and Decrement
var x = 5
x++ // returns 5, x becomes 6 (postfix)
++x // returns 7, x becomes 7 (prefix)
x-- // returns 7, x becomes 6 (postfix)
--x // returns 5, x becomes 5 (prefix)
Compound Assignment
var x = 10
x += 3 // 13
x -= 3 // 10
x *= 2 // 20
x /= 4 // 5
x %= 3 // 2
Ternary
var a = true ? 1 : 2 // 1
var b = false ? 1 : 2 // 2
var c = true ? (false ? 1 : 2) : 3 // 2 (nested)
Comma
The comma operator evaluates all expressions and returns the last.
var x = (1, 2, 3) // 3
In
Test whether a key exists in an object.
var o = {a: 1}
"a" in o // true
"b" in o // false
Delete
Remove a key from an object.
var o = {a: 1, b: 2}
delete o.a
"a" in o // false
o.b // 2
Property Access
Dot and Bracket
var o = {x: 10}
o.x // 10 (dot read)
o.x = 20 // dot write
o["x"] // 20 (bracket read)
var key = "x"
o[key] // 20 (computed bracket)
o["y"] = 30 // bracket write
Object as Key
Objects can be used as keys in other objects.
var k = {}
var o = {}
o[k] = 42
o[k] // 42
o[{}] // null (different object)
k in o // true
delete o[k]
k in o // false
Chained Access
var d = {a: {b: [1, {c: 99}]}}
d.a.b[1].c // 99
Arrays
Arrays are distinct from objects. They are ordered, numerically-indexed sequences.
var arr = [1, 2, 3]
arr[0] // 1
arr[2] = 10 // [1, 2, 10]
length(arr) // 3
Push and Pop
var a = [1, 2]
a[] = 3 // push: [1, 2, 3]
length(a) // 3
var v = a[] // pop: v is 3, a is [1, 2]
length(a) // 2
Objects
Objects are key-value records with prototype-based inheritance.
var point = {x: 10, y: 20}
point.x // 10
point["y"] // 20
Prototypes
// Create object with prototype
var parent = {x: 10}
var child = meme(parent)
child.x // 10 (inherited)
proto(child) // parent
// Override does not mutate parent
child.x = 20
parent.x // 10
Mixins
var p = {a: 1}
var m1 = {b: 2}
var m2 = {c: 3}
var child = meme(p, [m1, m2])
child.a // 1 (from prototype)
child.b // 2 (from mixin)
child.c // 3 (from mixin)
Control Flow
If / Else
var x = 0
if (true) x = 1
if (false) x = 2 else x = 3
if (false) x = 4
else if (true) x = 5
else x = 6
While
var i = 0
while (i < 5) i++
// break
i = 0
while (true) {
if (i >= 3) break
i++
}
// continue
var sum = 0
i = 0
while (i < 5) {
i++
if (i % 2 == 0) continue
sum += i
}
For
Variables cannot be declared in the for initializer. Declare them at the function body level.
var sum = 0
var i = 0
for (i = 0; i < 5; i++) sum += i
// break
sum = 0
i = 0
for (i = 0; i < 10; i++) {
if (i == 5) break
sum += i
}
// continue
sum = 0
i = 0
for (i = 0; i < 5; i++) {
if (i % 2 == 0) continue
sum += i
}
// nested
sum = 0
var j = 0
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
sum++
}
}
Functions
Function Expressions
var add = function(a, b) { return a + b }
add(2, 3) // 5
Arrow Functions
var double = x => x * 2
double(5) // 10
var sum = (a, b) => a + b
sum(2, 3) // 5
var block = x => {
var y = x * 2
return y + 1
}
block(5) // 11
Return
A function with no return returns null. An early return exits immediately.
var fn = function() { var x = 1 }
fn() // null
var fn2 = function() { return 1; return 2 }
fn2() // 1
Arguments
Extra arguments are ignored. Missing arguments are null.
var fn = function(a, b) { return a + b }
fn(1, 2, 3) // 3 (extra arg ignored)
var fn2 = function(a, b) { return a }
fn2(1) // 1 (b is null)
Immediately Invoked Function Expression
var r = (function(x) { return x * 2 })(21) // 42
Closures
Functions capture variables from their enclosing scope.
var make = function(x) {
return function(y) { return x + y }
}
var add5 = make(5)
add5(3) // 8
Captured variables can be mutated:
var counter = function() {
var n = 0
return function() { n = n + 1; return n }
}
var c = counter()
c() // 1
c() // 2
Recursion
var fact = function(n) {
if (n <= 1) return 1
return n * fact(n - 1)
}
fact(5) // 120
This Binding
When a function is called as a method, this refers to the object.
var obj = {
val: 10,
get: function() { return this.val }
}
obj.get() // 10
Currying
var f = function(a) {
return function(b) {
return function(c) { return a + b + c }
}
}
f(1)(2)(3) // 6
Identifiers
Identifiers can contain ? and ! characters, both as suffixes and mid-name.
var nil? = (x) => x == null
nil?(null) // true
nil?(42) // false
var set! = (x) => x + 1
set!(5) // 6
var is?valid = (x) => x > 0
is?valid(3) // true
var do!stuff = () => 42
do!stuff() // 42
The ? in an identifier is not confused with the ternary operator:
var nil? = (x) => x == null
var a = nil?(null) ? "yes" : "no" // "yes"
Type Checking
Type Functions
is_number(42) // true
is_text("hi") // true
is_logical(true) // true
is_object({}) // true
is_array([]) // true
is_function(function(){}) // true
is_null(null) // true
is_object([]) // false (array is not object)
is_array({}) // false (object is not array)
Truthiness
Falsy values: false, 0, "", null. Everything else is truthy.
if (0) ... // not entered
if ("") ... // not entered
if (null) ... // not entered
if (1) ... // entered
if ("hi") ... // entered
if ({}) ... // entered
if ([]) ... // entered
Immutability with Stone
The stone() function makes values permanently immutable.
var o = {x: 1}
is_stone(o) // false
stone(o)
is_stone(o) // true
o.x = 2 // disrupts!
Stone is deep — all nested objects and arrays are also frozen. This cannot be reversed.
Function Proxy
A function with two parameters (name, args) acts as a proxy when properties are accessed on it. Any method call on the function dispatches through the proxy.
var proxy = function(name, args) {
return `${name}:${length(args)}`
}
proxy.hello() // "hello:0"
proxy.add(1, 2) // "add:2"
proxy["method"]() // "method:0"
var m = "dynamic"
proxy[m]() // "dynamic:0"
For non-proxy functions, property access disrupts:
var fn = function() { return 1 }
fn.foo // disrupts
fn.foo = 1 // disrupts
Regex
Regex literals are written with forward slashes, with optional flags.
var r = /\d+/
var result = extract("abc123", r)
result[0] // "123"
var ri = /hello/i
var result2 = extract("Hello", ri)
result2[0] // "Hello"
Error Handling
ƿit uses disrupt and disruption for error handling. A disrupt signals that something went wrong. The disruption block attached to a function catches it.
var safe_divide = function(a, b) {
if (b == 0) disrupt
return a / b
} disruption {
print("something went wrong")
}
disrupt is a bare keyword — it does not carry a value. The disruption block knows that something went wrong, but not what.
Re-raising
A disruption block can re-raise by calling disrupt again:
var outer = function() {
var inner = function() { disrupt } disruption { disrupt }
inner()
} disruption {
// caught here after re-raise
}
outer()
Testing for Disruption
var should_disrupt = function(fn) {
var caught = false
var wrapper = function() {
fn()
} disruption {
caught = true
}
wrapper()
return caught
}
If an actor has an unhandled disruption, it crashes.
Self-Referencing Structures
Objects can reference themselves:
var o = {name: "root"}
o.self = o
o.self.self.name // "root"
Variable Shadowing
Inner functions can shadow outer variables:
var x = 10
var fn = function() {
var x = 20
return x
}
fn() // 20
x // 10