seanbutler/vo

0

stars

89

commits

C++

primary language

Sep 6, 2026

updated

README

VO

Vo is a small, expression-oriented programming language. The name comes from lingvo - Esperanto for language.

At its core, Vo provides a one universal data structure: the hash. Objects, modules, namespaces, prototypes, and constructors are all hashes. There are no classes, no arrays — only hashes, callables, loops, and recursion.

Everything is an expression. Blocks return their last value. There is no return keyword.

Syntax at a glance

// declaration
name = value                  // immutable, untyped
name : type = value           // immutable, typed
name : type := value          // mutable, typed
target := new_value           // reassignment

// identifiers — any UTF-8 sequence of letters, digits, _ and Unicode bytes
🐺 = "wolf"
speed🚀 : int := 0
café : int = 42

// hash (object / module / prototype)
point = { x : int = 0  y : int = 0 }

// callable (function)
add = @(a : int, b : int) { a + b }

// hash with constructor
Node = {
    value : int = 0
    next         = {}
    () = @(v : int, n) {
        self.value := v
        self.next  := n
    }
}
node = Node(42, {})           // clones Node, calls ()

// inheritance via _ delegation  (stdlib subtype helper)
# "lib/stdlib.vo"
Animal = {
    sound : string = "..."
    speak  = @() { self.sound }
    () = @(s : string) { self.sound := s }
}
Dog = subtype(Animal, { sound : string = "Woof" })
d = Dog("Rex")                // constructor found through _ chain
d.speak()                     // method inherited; self = d

// private slots — _ prefix hides from >>, merge, clone, and display
Counter = {
    _count : int = 0
    inc    = @() { self._count := self._count + 1 }
    value  = @() { self._count }
}

// conditional expression (else branch optional, returns nil if absent)
? x > 0 { "positive" } { "non-positive" }
? x > 0 { "positive" }

// logical NOT
! x
! (a == b)

// loop block — repeats until \ is executed
~{
    ? done { \ }
    body
}

// four canonical loop forms
~{ ? !cond { \ }   body }         // while cond
~{ body   ? !cond { \ } }         // do-while cond
~{ ? done  { \ }   body }         // until done
~{ body }                          // infinite (exit via \ only)

// member access
point.x
point.(key_expr)              // dynamic key

// hash iteration  (skips _ prefixed and () slots)
data >> @(k, v) { printf_s("%s\n", k) }

// import
# "lib/stdio.vo"

// foreign function binding
spec = { lib : string = "libc.so.6"  abi : string = "c"
         symbol : string = "puts"
         params = { p1 : string = "cstring" }
         returns : string = "int" }
puts = $$ spec

// built-in functions
ifloor(3.7)               // → 3  (double → int, floor semantics)
char_at("hello", 1)       // → "e"  (single-character string at index)

Key features

  • Hash as universal primitive — one data structure covers objects, modules, prototypes, and constructors
  • Expression-oriented — every construct produces a value; no return keyword
  • First-class callables — functions are values; closures capture their environment
  • Prototype-based OOP — calling a hash clones it and invokes its () constructor slot; self is bound in both constructors and methods
  • Inheritance via _ delegation — the _ slot chains member lookup to a parent hash at runtime; subtype in stdlib builds child hashes with one call
  • Private slots — any slot whose name begins with _ is hidden from >>, merge, clone, and display; directly accessible by name
  • Loop primitive~{ } is an infinite loop block; \ escapes it (lexically scoped, parse-time enforced); ! is logical NOT
  • C FFI via $$ — bind and call C library functions directly
  • No reserved words — only symbols; # import, @ callable literal, ? conditional, ~{ } loop, \ break, ! not, >> iteration, $$ FFI
  • Unicode identifiers — any UTF-8 byte sequence is a valid identifier name, including emoji and extended-ASCII glyphs (e.g. 🐺 = "wolf", speed🚀 : int := 0, café : int = 42)
  • Built-in functionsifloor(x) truncates a double to int; char_at(s, i) returns the single-character string at index i

Building

cd interp
cmake -B build
cmake --build build

Running

./build/vo program.vo
./build/vo program.vo --trace    # show token stream

Example — Sieve of Eratosthenes

# "lib/stdio.vo"

empty = { is_empty : int = 1 }

Node = {
    is_empty : int = 0
    value    : int = 0
    next            = empty
    () = @(v : int, n) { self.value := v  self.next := n }
}

range  = @(lo : int, hi : int) {
    ? lo > hi { empty } { Node(lo, range(lo + 1, hi)) }
}

filter = @(list, pred) {
    ? list.is_empty { empty } {
        ? pred(list.value) {
            Node(list.value, filter(list.next, pred))
        } {
            filter(list.next, pred)
        }
    }
}

sieve  = @(list) {
    ? list.is_empty { empty } {
        p : int = list.value
        Node(p, sieve(filter(list.next, @(n : int) { n % p != 0 })))
    }
}

print_list = @(list) {
    ? list.is_empty { } {
        printf_i("%d\n", list.value)
        print_list(list.next)
    }
}

print_list(sieve(range(2, 50)))

Example — Prototype OOP with inheritance

# "lib/stdio.vo"
# "lib/stdlib.vo"

// base — constructor + method
Animal = {
    sound : string = "..."
    speak  = @() { printf_s("%s\n", self.sound) }
    () = @(s : string) { self.sound := s }
}

// subtype inherits constructor and speak through _ chain
Dog = subtype(Animal, { sound : string = "Woof" })

// subtype with method override
Cat = subtype(Animal, {
    sound : string = "Meow"
    speak  = @() { printf_s("Cat says: %s\n", self.sound) }
})

// multi-level inheritance
Poodle = subtype(Dog, { size : string = "small" })

a = Animal("Grunt")   a.speak()          // Grunt
d = Dog("Rex")        d.speak()          // Rex
c = Cat("Whiskers")   c.speak()          // Cat says: Whiskers
p = Poodle("Fifi")    p.speak()          // Fifi  (constructor through two _ hops)

Structs — hashes with no () — inherit methods the same way:

Point = {
    x : int = 0
    y : int = 0
    dot = @(other) { self.x * other.x + self.y * other.y }
}

Point3 = subtype(Point, { z : int = 0 })

p = Point3()
p.x := 1  p.y := 2  p.z := 5
printf_i("%d\n", p.dot(p))   // 5

Private state with _ prefix — hidden from >>, merge, and clone:

Counter = {
    _count : int = 0
    inc    = @() { self._count := self._count + 1 }
    value  = @() { self._count }
}

c = Counter()
c.inc()  c.inc()  c.inc()
printf_i("%d\n", c.value())   // 3
// c._count accessible directly but invisible to iteration

Example — Extending the language via hashes

VO has no reserved words, so any vocabulary can be introduced as a plain hash of callables. Two patterns:

1 — Aliasing: logic and loop vocabulary libraries

Two library hashes, each importable independently. No language changes required.

// lib/logic.vo
logic = {
    not = @(x)    { !x }
    and = @(a, b) { ? a() { b() } { 0 } }
    or  = @(a, b) { ? a() { 1 }  { b() } }
}

// lib/loops.vo
loops = {
    while    = @(cond, body) { ~{ ? !cond() { \ }  body() } }
    do_while = @(body, cond) { ~{ body()  ? !cond() { \ } } }
    for      = @(lo : int, hi : int, body) {
        i : int := lo
        ~{ ? i >= hi { \ }  body(i)  i := i + 1 }
    }
}

# "lib/logic.vo"
# "lib/loops.vo"

? logic.not(0) { printf_s("%s\n", "not(0) is true") }
? logic.and(@() { 1 }, @() { 1 }) { printf_s("%s\n", "1 and 1") }

i : int := 1
loops.while(@() { i <= 5 }, @() { printf_i("%d\n", i)  i := i + 1 })

total : int := 0
loops.for(1, 11, @(n : int) { total := total + n })
printf_i("sum 1..10 = %d\n", total)    // 55

2 — New syntax: for loops as library callables

lib/loops.vo provides ascending, stepping, and descending for loops built on ~{ }.

# "lib/loops.vo"

loops.for(1, 6, @(i : int) { printf_i("%d\n", i) })              // 1 2 3 4 5
loops.for_step(0, 11, 2, @(i : int) { printf_i("%d\n", i) })     // 0 2 4 6 8 10
loops.for_down(1, 6, @(i : int) { printf_i("%d\n", i) })         // 5 4 3 2 1

Full source: interp/alias.vo

Source files

PathContents
interp/src/lexer/Tokeniser
interp/src/parser/Recursive-descent parser
interp/src/ast/AST node definitions
interp/src/interpreter/Tree-walking interpreter, FFI, environment
interp/lib/stdio.voprintf_s / printf_i bindings
interp/lib/stdlib.voclone, merge, subtype, without, has, size, rename, filter_map, map_values
interp/lib/metalib.voModule interface helpers — pick, omit, remap, public_api, exports_only
interp/lib/cstdio.voC stdio descriptor library
interp/lib/cstdlib.voC stdlib descriptor library (strlen, strcmp, strncmp, rand, …)
interp/lib/cstring.voString utility hash strstr.len, str.cmp, str.ncmp (wraps cstdlib)
interp/lib/cmath.voC math descriptor library — sin, cos, sqrt, pow, floor, ceil, and more
interp/lib/ffi.voFFI helper (bind_one, bind_lib)
interp/lib/logic.vologic hash — not, and, or (lazy boolean)
interp/lib/loops.voloops hash — while, do_while, for, for_step, for_down
interp/lib/vtkit.voTerminal double-buffered rendering — vtk hash (buffer_init, buffer_clear, buffer_vline, buffer_text, buffer_present, colour constants, …)
interp/alias.voExample: using logic and loops together

VO draws from several lineages. No single language shares all of its characteristics; the combination is what makes it distinct.

GOFAI Frames

Minsky's frame theory (1974) is a direct conceptual ancestor of VO's hash model. The correspondence is close enough to be more than coincidence — the lineage runs through Lisp frame systems, Smalltalk, Self, and Io before arriving at VO.

Frame conceptVO equivalent
Slot with default value{ name : type = default }
Inherited defaults via isa/ako link_ delegation chain
Procedural attachment (if-needed)callable member: method = @() { ... }
Frame initialisation procedure() constructor slot
Slot type constraint: type annotation

Where VO diverges from classic frames: no per-slot demons (if-added / if-removed triggers), no embedded semantic network, and no inference engine — VO uses the same structure for general-purpose computation rather than knowledge representation.

Prototype cloning model

Calling a hash clones it and invokes its () slot — the core OOP mechanism.

LanguageRelationship
SelfThe origin of prototype cloning. Objects are cloned, slots are universal storage — the closest philosophical match to VO's hash model
IoEverything is a message to a prototype; Object clone ≈ VO's Hash(). Minimal syntax, effectively no keywords
NewtonScriptApple Newton PDA language; prototype cloning with a frame/slot model almost identical to VO hashes
LuaTables as universal structure; metatables for OOP — same philosophy, more ceremony

Expression-oriented / implicit return

Blocks return their last value; there is no return keyword.

LanguageRelationship
RubyLast expression is the return value; blocks with {}
CoffeeScriptImplicit returns, cleaner JS semantics, {} object literals
RustLast expression returns; let/let mut mirrors VO's =/:=
ScalaFully expression-oriented; type annotation syntax name : Type is identical to VO
HaskellEverything is an expression; <- used for monadic binding
MoonScriptImplicit returns, compiles to Lua

:= mutable assignment operator

LanguageRelationship
Pascal / Ada:= is the assignment operator; = is comparison — the direct origin of VO's :=
Go:= for short variable declaration with inferred type
AlgolThe original source of := as assignment
Modula-2 / Oberon:= for assignment throughout

Type annotation syntax name : type

LanguageRelationship
Pascal / AdaThe origin of the name : type convention
Scala / Kotlinval x : Int = 7 — nearly identical to VO
Rustlet x : i32 = 7 — identical form
TypeScriptconst x : number = 7 — identical form

Symbol-only / no English keywords

LanguageRelationship
APLEntirely symbol-based; no English keywords at all — the extreme end of VO's direction
JAPL descendant; dense symbol vocabulary
Rebol / RedNo reserved words; everything is data; [] and {} as code — strong philosophical overlap
ForthNo keywords; all words are user-defined

Hash / map as the only data structure

LanguageRelationship
LuaTables are everything — arrays, objects, modules — same unifying principle
ClojureMaps as a core structure; everything is data
JanetLisp with first-class tables; lightweight and embeddable
Tcl{} as code blocks; minimal distinctions between code and data

FFI design

VO's $$ takes a hash descriptor — the binding spec is itself a first-class value.

LanguageRelationship
LuaJIT / FFIClosest match — C types declared as strings, called via ffi.C.func()
WrenForeign method binding via descriptors
Python ctypesSpec-as-data approach to C binding
Zig@cImport — compiler-level C interop via declarations

Contributors

seanbutler

89 commits

seanbutler/vo

0

stars

89

commits

C++

primary language

Sep 6, 2026

updated

README

VO

Vo is a small, expression-oriented programming language. The name comes from lingvo - Esperanto for language.

At its core, Vo provides a one universal data structure: the hash. Objects, modules, namespaces, prototypes, and constructors are all hashes. There are no classes, no arrays — only hashes, callables, loops, and recursion.

Everything is an expression. Blocks return their last value. There is no return keyword.

Syntax at a glance

// declaration
name = value                  // immutable, untyped
name : type = value           // immutable, typed
name : type := value          // mutable, typed
target := new_value           // reassignment

// identifiers — any UTF-8 sequence of letters, digits, _ and Unicode bytes
🐺 = "wolf"
speed🚀 : int := 0
café : int = 42

// hash (object / module / prototype)
point = { x : int = 0  y : int = 0 }

// callable (function)
add = @(a : int, b : int) { a + b }

// hash with constructor
Node = {
    value : int = 0
    next         = {}
    () = @(v : int, n) {
        self.value := v
        self.next  := n
    }
}
node = Node(42, {})           // clones Node, calls ()

// inheritance via _ delegation  (stdlib subtype helper)
# "lib/stdlib.vo"
Animal = {
    sound : string = "..."
    speak  = @() { self.sound }
    () = @(s : string) { self.sound := s }
}
Dog = subtype(Animal, { sound : string = "Woof" })
d = Dog("Rex")                // constructor found through _ chain
d.speak()                     // method inherited; self = d

// private slots — _ prefix hides from >>, merge, clone, and display
Counter = {
    _count : int = 0
    inc    = @() { self._count := self._count + 1 }
    value  = @() { self._count }
}

// conditional expression (else branch optional, returns nil if absent)
? x > 0 { "positive" } { "non-positive" }
? x > 0 { "positive" }

// logical NOT
! x
! (a == b)

// loop block — repeats until \ is executed
~{
    ? done { \ }
    body
}

// four canonical loop forms
~{ ? !cond { \ }   body }         // while cond
~{ body   ? !cond { \ } }         // do-while cond
~{ ? done  { \ }   body }         // until done
~{ body }                          // infinite (exit via \ only)

// member access
point.x
point.(key_expr)              // dynamic key

// hash iteration  (skips _ prefixed and () slots)
data >> @(k, v) { printf_s("%s\n", k) }

// import
# "lib/stdio.vo"

// foreign function binding
spec = { lib : string = "libc.so.6"  abi : string = "c"
         symbol : string = "puts"
         params = { p1 : string = "cstring" }
         returns : string = "int" }
puts = $$ spec

// built-in functions
ifloor(3.7)               // → 3  (double → int, floor semantics)
char_at("hello", 1)       // → "e"  (single-character string at index)

Key features

  • Hash as universal primitive — one data structure covers objects, modules, prototypes, and constructors
  • Expression-oriented — every construct produces a value; no return keyword
  • First-class callables — functions are values; closures capture their environment
  • Prototype-based OOP — calling a hash clones it and invokes its () constructor slot; self is bound in both constructors and methods
  • Inheritance via _ delegation — the _ slot chains member lookup to a parent hash at runtime; subtype in stdlib builds child hashes with one call
  • Private slots — any slot whose name begins with _ is hidden from >>, merge, clone, and display; directly accessible by name
  • Loop primitive~{ } is an infinite loop block; \ escapes it (lexically scoped, parse-time enforced); ! is logical NOT
  • C FFI via $$ — bind and call C library functions directly
  • No reserved words — only symbols; # import, @ callable literal, ? conditional, ~{ } loop, \ break, ! not, >> iteration, $$ FFI
  • Unicode identifiers — any UTF-8 byte sequence is a valid identifier name, including emoji and extended-ASCII glyphs (e.g. 🐺 = "wolf", speed🚀 : int := 0, café : int = 42)
  • Built-in functionsifloor(x) truncates a double to int; char_at(s, i) returns the single-character string at index i

Building

cd interp
cmake -B build
cmake --build build

Running

./build/vo program.vo
./build/vo program.vo --trace    # show token stream

Example — Sieve of Eratosthenes

# "lib/stdio.vo"

empty = { is_empty : int = 1 }

Node = {
    is_empty : int = 0
    value    : int = 0
    next            = empty
    () = @(v : int, n) { self.value := v  self.next := n }
}

range  = @(lo : int, hi : int) {
    ? lo > hi { empty } { Node(lo, range(lo + 1, hi)) }
}

filter = @(list, pred) {
    ? list.is_empty { empty } {
        ? pred(list.value) {
            Node(list.value, filter(list.next, pred))
        } {
            filter(list.next, pred)
        }
    }
}

sieve  = @(list) {
    ? list.is_empty { empty } {
        p : int = list.value
        Node(p, sieve(filter(list.next, @(n : int) { n % p != 0 })))
    }
}

print_list = @(list) {
    ? list.is_empty { } {
        printf_i("%d\n", list.value)
        print_list(list.next)
    }
}

print_list(sieve(range(2, 50)))

Example — Prototype OOP with inheritance

# "lib/stdio.vo"
# "lib/stdlib.vo"

// base — constructor + method
Animal = {
    sound : string = "..."
    speak  = @() { printf_s("%s\n", self.sound) }
    () = @(s : string) { self.sound := s }
}

// subtype inherits constructor and speak through _ chain
Dog = subtype(Animal, { sound : string = "Woof" })

// subtype with method override
Cat = subtype(Animal, {
    sound : string = "Meow"
    speak  = @() { printf_s("Cat says: %s\n", self.sound) }
})

// multi-level inheritance
Poodle = subtype(Dog, { size : string = "small" })

a = Animal("Grunt")   a.speak()          // Grunt
d = Dog("Rex")        d.speak()          // Rex
c = Cat("Whiskers")   c.speak()          // Cat says: Whiskers
p = Poodle("Fifi")    p.speak()          // Fifi  (constructor through two _ hops)

Structs — hashes with no () — inherit methods the same way:

Point = {
    x : int = 0
    y : int = 0
    dot = @(other) { self.x * other.x + self.y * other.y }
}

Point3 = subtype(Point, { z : int = 0 })

p = Point3()
p.x := 1  p.y := 2  p.z := 5
printf_i("%d\n", p.dot(p))   // 5

Private state with _ prefix — hidden from >>, merge, and clone:

Counter = {
    _count : int = 0
    inc    = @() { self._count := self._count + 1 }
    value  = @() { self._count }
}

c = Counter()
c.inc()  c.inc()  c.inc()
printf_i("%d\n", c.value())   // 3
// c._count accessible directly but invisible to iteration

Example — Extending the language via hashes

VO has no reserved words, so any vocabulary can be introduced as a plain hash of callables. Two patterns:

1 — Aliasing: logic and loop vocabulary libraries

Two library hashes, each importable independently. No language changes required.

// lib/logic.vo
logic = {
    not = @(x)    { !x }
    and = @(a, b) { ? a() { b() } { 0 } }
    or  = @(a, b) { ? a() { 1 }  { b() } }
}

// lib/loops.vo
loops = {
    while    = @(cond, body) { ~{ ? !cond() { \ }  body() } }
    do_while = @(body, cond) { ~{ body()  ? !cond() { \ } } }
    for      = @(lo : int, hi : int, body) {
        i : int := lo
        ~{ ? i >= hi { \ }  body(i)  i := i + 1 }
    }
}

# "lib/logic.vo"
# "lib/loops.vo"

? logic.not(0) { printf_s("%s\n", "not(0) is true") }
? logic.and(@() { 1 }, @() { 1 }) { printf_s("%s\n", "1 and 1") }

i : int := 1
loops.while(@() { i <= 5 }, @() { printf_i("%d\n", i)  i := i + 1 })

total : int := 0
loops.for(1, 11, @(n : int) { total := total + n })
printf_i("sum 1..10 = %d\n", total)    // 55

2 — New syntax: for loops as library callables

lib/loops.vo provides ascending, stepping, and descending for loops built on ~{ }.

# "lib/loops.vo"

loops.for(1, 6, @(i : int) { printf_i("%d\n", i) })              // 1 2 3 4 5
loops.for_step(0, 11, 2, @(i : int) { printf_i("%d\n", i) })     // 0 2 4 6 8 10
loops.for_down(1, 6, @(i : int) { printf_i("%d\n", i) })         // 5 4 3 2 1

Full source: interp/alias.vo

Source files

PathContents
interp/src/lexer/Tokeniser
interp/src/parser/Recursive-descent parser
interp/src/ast/AST node definitions
interp/src/interpreter/Tree-walking interpreter, FFI, environment
interp/lib/stdio.voprintf_s / printf_i bindings
interp/lib/stdlib.voclone, merge, subtype, without, has, size, rename, filter_map, map_values
interp/lib/metalib.voModule interface helpers — pick, omit, remap, public_api, exports_only
interp/lib/cstdio.voC stdio descriptor library
interp/lib/cstdlib.voC stdlib descriptor library (strlen, strcmp, strncmp, rand, …)
interp/lib/cstring.voString utility hash strstr.len, str.cmp, str.ncmp (wraps cstdlib)
interp/lib/cmath.voC math descriptor library — sin, cos, sqrt, pow, floor, ceil, and more
interp/lib/ffi.voFFI helper (bind_one, bind_lib)
interp/lib/logic.vologic hash — not, and, or (lazy boolean)
interp/lib/loops.voloops hash — while, do_while, for, for_step, for_down
interp/lib/vtkit.voTerminal double-buffered rendering — vtk hash (buffer_init, buffer_clear, buffer_vline, buffer_text, buffer_present, colour constants, …)
interp/alias.voExample: using logic and loops together

VO draws from several lineages. No single language shares all of its characteristics; the combination is what makes it distinct.

GOFAI Frames

Minsky's frame theory (1974) is a direct conceptual ancestor of VO's hash model. The correspondence is close enough to be more than coincidence — the lineage runs through Lisp frame systems, Smalltalk, Self, and Io before arriving at VO.

Frame conceptVO equivalent
Slot with default value{ name : type = default }
Inherited defaults via isa/ako link_ delegation chain
Procedural attachment (if-needed)callable member: method = @() { ... }
Frame initialisation procedure() constructor slot
Slot type constraint: type annotation

Where VO diverges from classic frames: no per-slot demons (if-added / if-removed triggers), no embedded semantic network, and no inference engine — VO uses the same structure for general-purpose computation rather than knowledge representation.

Prototype cloning model

Calling a hash clones it and invokes its () slot — the core OOP mechanism.

LanguageRelationship
SelfThe origin of prototype cloning. Objects are cloned, slots are universal storage — the closest philosophical match to VO's hash model
IoEverything is a message to a prototype; Object clone ≈ VO's Hash(). Minimal syntax, effectively no keywords
NewtonScriptApple Newton PDA language; prototype cloning with a frame/slot model almost identical to VO hashes
LuaTables as universal structure; metatables for OOP — same philosophy, more ceremony

Expression-oriented / implicit return

Blocks return their last value; there is no return keyword.

LanguageRelationship
RubyLast expression is the return value; blocks with {}
CoffeeScriptImplicit returns, cleaner JS semantics, {} object literals
RustLast expression returns; let/let mut mirrors VO's =/:=
ScalaFully expression-oriented; type annotation syntax name : Type is identical to VO
HaskellEverything is an expression; <- used for monadic binding
MoonScriptImplicit returns, compiles to Lua

:= mutable assignment operator

LanguageRelationship
Pascal / Ada:= is the assignment operator; = is comparison — the direct origin of VO's :=
Go:= for short variable declaration with inferred type
AlgolThe original source of := as assignment
Modula-2 / Oberon:= for assignment throughout

Type annotation syntax name : type

LanguageRelationship
Pascal / AdaThe origin of the name : type convention
Scala / Kotlinval x : Int = 7 — nearly identical to VO
Rustlet x : i32 = 7 — identical form
TypeScriptconst x : number = 7 — identical form

Symbol-only / no English keywords

LanguageRelationship
APLEntirely symbol-based; no English keywords at all — the extreme end of VO's direction
JAPL descendant; dense symbol vocabulary
Rebol / RedNo reserved words; everything is data; [] and {} as code — strong philosophical overlap
ForthNo keywords; all words are user-defined

Hash / map as the only data structure

LanguageRelationship
LuaTables are everything — arrays, objects, modules — same unifying principle
ClojureMaps as a core structure; everything is data
JanetLisp with first-class tables; lightweight and embeddable
Tcl{} as code blocks; minimal distinctions between code and data

FFI design

VO's $$ takes a hash descriptor — the binding spec is itself a first-class value.

LanguageRelationship
LuaJIT / FFIClosest match — C types declared as strings, called via ffi.C.func()
WrenForeign method binding via descriptors
Python ctypesSpec-as-data approach to C binding
Zig@cImport — compiler-level C interop via declarations

Contributors

seanbutler

89 commits

Languages

C++

95.1%

Shell

3.6%

CMake

1.3%