658 lines
12 KiB
Markdown
658 lines
12 KiB
Markdown
---
|
|
title: "ƿit Language"
|
|
description: "Syntax, types, operators, and built-in functions"
|
|
weight: 10
|
|
type: "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 `do` blocks.
|
|
|
|
```javascript
|
|
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** — `true` or `false`
|
|
- **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
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
5 == 5 // true
|
|
5 != 6 // true
|
|
3 < 5 // true
|
|
5 > 3 // true
|
|
3 <= 3 // true
|
|
5 >= 5 // true
|
|
```
|
|
|
|
### Logical
|
|
|
|
```javascript
|
|
true && true // true
|
|
true && false // false
|
|
false || true // true
|
|
false || false // false
|
|
!true // false
|
|
!false // true
|
|
```
|
|
|
|
Logical operators short-circuit:
|
|
|
|
```javascript
|
|
var called = false
|
|
var fn = function() { called = true; return true }
|
|
var r = false && fn() // fn() not called
|
|
r = true || fn() // fn() not called
|
|
```
|
|
|
|
### Bitwise
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
+5 // 5
|
|
-5 // -5
|
|
-(-5) // 5
|
|
```
|
|
|
|
### Increment and Decrement
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
var x = 10
|
|
x += 3 // 13
|
|
x -= 3 // 10
|
|
x *= 2 // 20
|
|
x /= 4 // 5
|
|
x %= 3 // 2
|
|
```
|
|
|
|
### Ternary
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
var x = (1, 2, 3) // 3
|
|
```
|
|
|
|
### In
|
|
|
|
Test whether a key exists in an object.
|
|
|
|
```javascript
|
|
var o = {a: 1}
|
|
"a" in o // true
|
|
"b" in o // false
|
|
```
|
|
|
|
### Delete
|
|
|
|
Remove a key from an object.
|
|
|
|
```javascript
|
|
var o = {a: 1, b: 2}
|
|
delete o.a
|
|
"a" in o // false
|
|
o.b // 2
|
|
```
|
|
|
|
## Property Access
|
|
|
|
### Dot and Bracket
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
var arr = [1, 2, 3]
|
|
arr[0] // 1
|
|
arr[2] = 10 // [1, 2, 10]
|
|
length(arr) // 3
|
|
```
|
|
|
|
### Push and Pop
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
var point = {x: 10, y: 20}
|
|
point.x // 10
|
|
point["y"] // 20
|
|
```
|
|
|
|
### Prototypes
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
var add = function(a, b) { return a + b }
|
|
add(2, 3) // 5
|
|
```
|
|
|
|
### Arrow Functions
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
var fn = function() { var x = 1 }
|
|
fn() // null
|
|
|
|
var fn2 = function() { return 1; return 2 }
|
|
fn2() // 1
|
|
```
|
|
|
|
### Arguments
|
|
|
|
Functions can have at most **4 parameters**. Use a record to pass more values.
|
|
|
|
Extra arguments are ignored. Missing arguments are `null`.
|
|
|
|
```javascript
|
|
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)
|
|
|
|
// More than 4 parameters — use a record
|
|
var draw = function(shape, opts) {
|
|
// opts.x, opts.y, opts.color, ...
|
|
}
|
|
```
|
|
|
|
### Immediately Invoked Function Expression
|
|
|
|
```javascript
|
|
var r = (function(x) { return x * 2 })(21) // 42
|
|
```
|
|
|
|
### Closures
|
|
|
|
Functions capture variables from their enclosing scope.
|
|
|
|
```javascript
|
|
var make = function(x) {
|
|
return function(y) { return x + y }
|
|
}
|
|
var add5 = make(5)
|
|
add5(3) // 8
|
|
```
|
|
|
|
Captured variables can be mutated:
|
|
|
|
```javascript
|
|
var counter = function() {
|
|
var n = 0
|
|
return function() { n = n + 1; return n }
|
|
}
|
|
var c = counter()
|
|
c() // 1
|
|
c() // 2
|
|
```
|
|
|
|
### Recursion
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
var obj = {
|
|
val: 10,
|
|
get: function() { return this.val }
|
|
}
|
|
obj.get() // 10
|
|
```
|
|
|
|
### Currying
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
var nil? = (x) => x == null
|
|
var a = nil?(null) ? "yes" : "no" // "yes"
|
|
```
|
|
|
|
## Type Checking
|
|
|
|
### Type Functions
|
|
|
|
```javascript
|
|
is_number(42) // true
|
|
is_text("hi") // true
|
|
is_logical(true) // true
|
|
is_object({}) // true (records only)
|
|
is_array([]) // true
|
|
is_function(function(){}) // true
|
|
is_null(null) // true
|
|
is_object([]) // false (arrays are not records)
|
|
is_object("hello") // false (text is not a record)
|
|
is_array({}) // false (records are not arrays)
|
|
```
|
|
|
|
### Truthiness
|
|
|
|
Falsy values: `false`, `0`, `""`, `null`. Everything else is truthy.
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
var fn = function() { return 1 }
|
|
fn.foo // disrupts
|
|
fn.foo = 1 // disrupts
|
|
```
|
|
|
|
## Regex
|
|
|
|
Regex literals are written with forward slashes, with optional flags.
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
var outer = function() {
|
|
var inner = function() { disrupt } disruption { disrupt }
|
|
inner()
|
|
} disruption {
|
|
// caught here after re-raise
|
|
}
|
|
outer()
|
|
```
|
|
|
|
### Testing for Disruption
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
var o = {name: "root"}
|
|
o.self = o
|
|
o.self.self.name // "root"
|
|
```
|
|
|
|
## Variable Shadowing
|
|
|
|
Inner functions can shadow outer variables:
|
|
|
|
```javascript
|
|
var x = 10
|
|
var fn = function() {
|
|
var x = 20
|
|
return x
|
|
}
|
|
fn() // 20
|
|
x // 10
|
|
```
|