Sitelet https://github.com/katsu-def/GybinScript
Skip to content

Latest commit

 

History

24 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GybinScript — User Manual

Ask DeepWiki

Read this manual in Spanish (README-ES.md)

Version: 1.7
File extension: .gbn
Interpreter: Core/Gybin \ /usr/bin/Gybin Execution: Gybin (File path: My_script.gbn)

! You can also declare the interpreter on the first line of your code and execute it like any program (Linux only). Ex:

#!/usr/bin/Gybin -- Parser path

$print("Hello!") 
chmod +x My_script.gbn
./My_script.gbn

Run setup-linux to configure the Gybin launcher in '/usr/bin' Run setup-termux to set up the language in a Termux environment


Table of Contents

  1. Introduction
  2. Console Execution
  3. Data Types
  4. Variables and Constants
  5. Comments
  6. Operators
  7. Functions
  8. Classes
  9. Inheritance
  10. Enums
  11. Arrays
  12. Dictionaries
  13. Control Structures
  14. Error Handling
  15. Pointers
  16. Events
  17. Modules and Imports
  18. Memory Management
  19. Native Functions (built-ins)
  20. Standard Library (stdutils)
  21. Compilation to Executable
  22. Warnings and Static Analysis
  23. Rules and Best Practices

1. Introduction

GybinScript is a statically typed and interpreted scripting language with controlled memory management. It is designed to be expressive and predictable: all variables must be declared with a type, blocks are closed with end, and the $ sign is the mandatory prefix to read or modify any variable, or call any function.


2. Console Execution

Gybin my_script.gbn [options]

Available Options

Flag Description
--sm Displays memory status upon completing execution
--pr Automatically prints all values returned by return
--t Displays execution time
--tr Displays each executed line instead of normal output
--c Compiles the script to an executable — see §21. Does not execute the script
--fc Unconditionally compiles the script, even if it has errors — see §21
--n NAME Custom name for the compiled executable (only with --c/--fc)
--ad PATH[=DEST] Packages an extra file/folder into the executable; can be repeated (only with --c/--fc)
--i ICON_PATH Icon for the compiled executable (only with --c/--fc)
--w Enables warning messages (static analysis)
--nc Suppresses all standard output (errors are still displayed)
--nch Disables caching for this run: #onready functions always execute and $preload always reads from disk — see §7 and §18

Example

Gybin game.gbn --sm --w --t

3. Data Types

GybinScript features six primitive types and two collection types:

Type Description Example
int Integer 20
float Decimal 3.1416
str[N] String with a maximum length N "Carlos"
bool Boolean true / false
any No type restriction —
NULL Null value / absence of value NULL
array[T,...] Typed list of elements [1, 2, 3]
dict[V,...] Typed dictionary of values {"a": 1}
ptr Pointer/reference to another variable, constant, function, class, or event; can be narrowed with ptr[kinds][types] (see §15) $$hp

Automatic Coercions:

  • An int assigned to a float is automatically converted to float.
  • A float without a decimal part assigned to an int is converted to int.
  • A float with decimals assigned to an int produces a type error.
var age: int = 20
var pi: float = 3.1416
var alive: bool = true
var name: str[16] = "Carlos"

$print($age)
$print($pi)
$print($alive)
$print($name)

Bit Width for int / float

Both numeric types use 64 bits by default. Instead of introducing separate type names for smaller bit widths, the same [N] size syntax used by str is reused to select a narrower width:

var integer: int[16] = 1000       -- Signed 16-bit int: -32768..32767
var decimal: float[32] = 3.14    -- 32-bit float (IEEE-754 single precision)
var other: float [32] = 2.71      -- A space before the bracket is also accepted
Type Valid Widths
int[N] 8, 16, 32, 64
float[N] 16, 32, 64

A value that does not fit into the declared width results in an immediate error, along with a suggestion for a width that would contain it:

var small: int[8] = 500  -- error: does not fit in a signed 8-bit int (range -128..127). Use int[16] or larger.

Coercion between int and float requires bit widths to match. Implicit int <-> float conversions (see "Automatic Coercions" above) only occur when both sides have the same declared bit width — or when neither specifies one (both default to 64):

var float_val: float = 3.14
var int_val: int[32] = $float_val   -- Error: coercion requires equal widths (64 vs 32)

var float32_val: float[32] = 3.14
var int32_val: int[32] = $float32_val  -- OK: both are 32 bits

This applies equally to constants as well as function parameters and return types:

func sum(a: int[16], b: int[16]) -> int[16]
    return a + b
end

Element types of array/dict never track a specific bit width — array[int] accepts int values of any width mixed together, since a container does not have its own per-element type annotation. Writing array[int[16]] is rejected as ambiguous (it is unclear to which elements it would apply); use a plain array[int] instead.

f-strings

String literals prefixed with f support {expression} interpolation, including format specifiers and conversions:

var name: str = "Ana"
$print(f"Hello {$name}!")
$print(f"{3.14159:.2f}")   -- 3.14

4. Variables and Constants

Variable Declaration

The basic syntax is var name: type = value. The $ sign is used to read or modify the variable after declaring it.

var hp: int = 100
$hp = 50

Size in Name (Shorthand)

You can specify the maximum size directly in the variable name instead of the type annotation:

var buffer[128]: str = "hello"

Multiple Types

A variable can accept more than one type by separating them with commas. Use sparingly:

var data: int,str = 10
$data = "text"

Constants

Constants are declared with const and cannot be reassigned:

const MAX_HP: int = 200

Attempting to modify a constant produces a type error (Immutable constant).

#onready

The #onready modifier declares a variable before the program starts executing, useful for early dependency initialization:

#onready var config: str[64] = "default"

When used, reassignments of the same value are also prevented.

#onready can also be placed before a function, where it caches the function's results instead — see Cached Functions.

#reserved

#reserved is used to declare script elements private, meaning they cannot be used outside the script where they were declared:

#reserved var critical: bool = false

#public

#public is the counterpart to #reserved regarding readability. It changes nothing functionally — a variable, function, or class is already public by default — existing solely to declare that intent explicitly in code:

#public var health: int = 100

#inmutable

#inmutable behaves like const for direct assignment — $name = value throws the same error as a constant — but unlike a true const, its value can be changed indirectly via a pointer:

#inmutable var x: int = 5
$x = 10              -- error: 'x' is #inmutable

var p: ptr = $$x
$p.value = 99         -- OK — x is now 99

A true const remains completely locked down even through a pointer; #inmutable is the version that allows that single deliberate loophole.

NULL as Empty Value

NULL represents the absence of a value. Objects and complex variables are declared with NULL when they do not yet hold definitive content:

var name: str[32] = NULL
var hp: int = NULL

! The interpreter ignores objects with a NULL value until assigned one. In arrays and dicts, it is better to initialize with [] or {} instead of NULL if you plan to append elements immediately.


5. Comments

Single-line Comment

Starts with -- and extends to the end of the line:

var x: int = 5 -- this is a comment

Block Comment

Delimited by !* at the start and !* at the end. Can span multiple lines:

!* This is a multi-line
   comment !*

Block comments do not nest: the second !* closes the block opened by the first.


6. Operators

Arithmetic

Operator Operation
+ Addition
- Subtraction
* Multiplication
/ Division
% Modulo
** Exponentiation

Comparison

Operator Meaning
== Equal
!= Not equal
< Less than
<= Less than or equal to
> Greater than
>= Greater than or equal to

Logical

Operator Meaning
and Logical AND
or Logical OR
not Negation
is Data comparation

Compound Assignment

$x += 5
$x -= 2
$x *= 3
$x /= 4

7. Functions

Declaration

func name(param1: type, param2: type) -> return_type
    -- body
end

The return type is mandatory. Use NULL if the function returns nothing:

func greet(name: str[32]) -> NULL
    $print("Hello " + $name)
end

Function with Return Value

func add(a: int, b: int) -> int
    return $a + $b
end

var result: int = $add(10, 20)
$print($result)

Calling Functions

All calls must be prefixed with $:

$greet("Carlos")
$print($multiply($result, 2))

Explicit Bit Widths in Parameters and Return Types

int[N]/float[N] (see §3) also work in function signatures:

func sum(a: int[16], b: int[16]) -> int[16]
    return $a + $b
end

! The same width-matching rule applies to arguments: callers passing a variable declared with a different explicit bit width than the parameter must convert it explicitly first.

Cached Functions (#onready func)

Placing #onready before a function caches its results: the first call with a given set of arguments runs the body, and any later call with exactly the same arguments returns the stored result without running it again.

#onready func square(n: int) -> int
    $print("computing...")
    return $n * $n
end

$print($square(4))   -- computing... / 16
$print($square(4))   -- 16 (from cache)
$print($square(5))   -- computing... / 25
  • Every distinct set of arguments has its own cached result.
  • On a class method, each instance keeps its own cache.
  • A cached call does not repeat the function's side effects (prints, file writes...), so it fits functions that return the same result for the same arguments.
  • Run with --nch to disable caching entirely for that execution.

Rest Parameters (!)

A parameter whose name starts with ! collects the arguments it receives into an array:

func sum(!values[10]: int) -> int
    var total: int = 0
    for n in $values
        $total += $n
    end
    return $total
end

$print($sum(1, 2, 3, 4))   -- 10
$print($sum())             -- 0
  • [10] (optional) sets how many values it can hold, and : int (optional) the type of each one — the same rules as array[int][10].
  • A function can have only one ! parameter, and it must be the last one. Any parameters before it receive their arguments first, and the ! parameter collects the rest:
func report(title: str, !lines: str) -> NULL
    $print($title)
    for line in $lines
        $print(" - " + $line)
    end
end

$report("Log", "started", "loaded", "done")

Named Parameters (?)

A parameter marked with ? is filled by name when calling the function, written as $name=value:

func set_modulate(?r: float, ?g: float, ?b: float, a: float) -> color
    return $color($r, $g, $b, $a)
end

var c: color = $set_modulate(1.0, $r=0.6, $g=0.3, $b=0.9)
  • The $ before the name is required.
  • Only parameters marked with ? can be given by name; using a name that doesn't match one is an error.
  • A ? parameter that isn't given stays NULL.
  • Class constructors work the same way: if init declares ?hp: int, then $Player($hp=50) fills it.

Named Rest Parameters (?!)

Combining both markers makes a named parameter that collects an array. Unlike a plain !, a function may declare several ?! parameters, since each one is filled by name with its own array:

func groups(?!a: int, ?!b: int) -> NULL
    $print($a)   -- [1, 2, 3]
    $print($b)   -- [4, 5]
end

$groups($a=[1, 2, 3], $b=[4, 5])

A plain ! parameter can't be combined with ?! parameters in the same function.

Main Function (init) and run

The run keyword executes the init() function defined in the global scope. This is the standard way to structure a program's entry point:

func init() -> NULL
    $print("Program started")
end

run

The init() function inside a class serves as that class's constructor and does not collide with global init() because they are used in different contexts.


8. Classes

Declaration

class ClassName
    var field: type = NULL

    func init(self, param: type) -> NULL
        $self.field = $param
    end

end

self refers to the current instance and is automatically passed as the first argument when a method is invoked. You do not pass it when instantiating:

var p: any = $Player("Juan")
$print($p.name)
$print($p.hp)

Methods

Methods are defined inside the class just like functions, accepting self as their first parameter:

class Player
    var hp: int = NULL

    func init(self) -> NULL
        $self.hp = 100
    end

    func damage(self, amount: int) -> NULL
        $self.hp -= $amount
    end

end

var p: Player = $Player()
$p.damage(30)
$print($p.hp)  -- 70

Field Access

Fields are accessed using a dot (.) without $:

$print($p.name)
$p.hp = 50

9. Inheritance

A class can extend another using extends:

class Entity
    var hp: int = NULL

    func init(self) -> NULL
        $self.hp = 100
    end

end

class Enemy extends Entity
    var damage: int = NULL

    func __init__(self) -> NULL
        $self.hp = 50
        $self.damage = 10
    end

end

var e: Enemy = $Enemy()
$print($e.hp)     -- 50
$print($e.damage) -- 10

! If the parent class already defines an init() method, the child class must use __init__() instead to prevent name collisions. The child class inherits all fields and methods from the parent class.


10. Enums

Enums group named constants under a common type:

enum Direction = {UP, DOWN, LEFT, RIGHT}

var dir: int = Direction.UP
$print($dir)  -- 0

Values are automatically assigned starting from 0. They are accessed using EnumName.MEMBER.

.null, Iteration, and in

Every enum has an implicit .null member — a sentinel signifying "nothing from this enumeration has been saved yet." It is separate from declared enum members and never counted among them:

enum Direction = {UP, DOWN, LEFT, RIGHT}

var current: Direction = Direction.null
$print($current)  -- distinct from the value of any declared member

An enum is directly iterable using for ... in and supports the in membership operator — both iterate strictly over declared members, never .null:

for d in $Direction
    $print(d)          -- UP, DOWN, LEFT, RIGHT — never .null
end

if Direction.UP in $Direction
    $print("UP is a member")
end

Enums in Classes

enum ItemType = {WEAPON, ARMOR, CONSUMABLE}

class Item
    var name: str[32] = NULL
    var type: any = NULL

    func init(self, name: str[32], type: ItemType) -> NULL
        $self.name = $name
        $self.type = $type
    end

end

var sword: Item = $Item("Sword", $ItemType.WEAPON)

11. Arrays

Declaration

var numbers: array[int] = []

Use [] to initialize an empty array. Using NULL without assigning elements immediately will cause an error when trying to append.

Array Methods

Method Description
.append(value) Appends an element to the end
.remove(index) Removes the element at the given index
.size() Returns the number of elements
.duplicate() Returns a copy of the array
.push_back(value) Alias for append
$numbers.append(10)
$numbers.append(20)
$numbers.append(30)

$print($numbers)       -- [10, 20, 30]

$numbers[1] = 50
$print($numbers)       -- [10, 50, 30]

Arrays with Maximum Size

var list: array[int][10] = []

This caps the array at a maximum of 10 elements.

Arrays of Objects

var enemies: array[any] = []
$enemies.append($Enemy(10))
$print($enemies[2].hp)

12. Dictionaries

Declaration

var inventory: dict[int] = {}

Basic Usage

$inventory["Potion"] = 5
$inventory["Sword"] = 1
$print($inventory)  -- {"Potion": 5, "Sword": 1}

Dictionaries of Objects

var inventory: dict[any] = {}
$inventory["weapon"] = Item("Sword")
$print($inventory["weapon"].name)

Dictionary Methods

Method Description
.size() Returns the number of entries
.remove(key) Removes the entry with that key
.duplicate() Returns a copy of the dictionary

13. Control Structures

Conditional if / elseif / else

if $x > 10
    $print("greater")
elseif $x == 10
    $print("equal")
else
    $print("smaller")
end

elif is also accepted as a shorthand for elseif.

while Loop

var i: int = 0
while $i < 10
    $print($i)
    $i += 1
end

for in Loop

Iterates over arrays, ranges, or dictionaries:

for item in $enemies
    $print($item.hp)
end

for n in $range(5)
    $print($n)
end

match Conditional

Compares a subject against several potential values. Each case opens with case value1, value2 (multiple comma-separated labels are permitted). Only the first matching case executes (or else, if present); there is no fallthrough between cases.

match $day
    case 1
        $print("Monday")
    case 2, 3
        $print("Tuesday or Wednesday")
    else
        $print("Other day")
end

Case labels can be any evaluable expression: literals, variables, enum members (Color.RED), or even array/dict literals ([1, 2, 3], {"a": 1}). If nothing matches and there is no else, nothing happens — no error is raised.

Loop Control Keywords

Keyword Behavior
break Exits the current loop
continue Skips to the next iteration
loop Restarts the current iteration from the beginning
pass Does nothing (empty block placeholder)

loop is easily confused with continue because in a simple while loop they look similar, but they are not identical: continue advances to the next iteration (the next item of a for, or rechecks a while condition), whereas loop re-executes the same iteration from the start, with its state left exactly as it was — the same item in for, without rechecking the while condition — until it finishes normally.

var attempts: int = 0
for item in [10, 20]
    $attempts += 1
    $print(f"trying {item}")
    if item == 10 and $attempts < 2
        loop  -- restarts with item still at 10, not 20
    end
end
-- prints: trying 10 / trying 10 / trying 20

await

Pauses execution until a condition becomes true (polls every 10ms):

await $ready == true

14. Error Handling

try
    -- code that may fail
catch
    -- code executed if an error occurs
end

except is also accepted as an alias for catch:

try
    var x: int = $int("not_a_number")
except
    $print("A conversion error occurred")
end

Without any type, catch/except handles every error. An empty catch/except body is valid: it silences the error and does nothing else.

Catching Specific Error Types

Follow catch/except with one or more error types to handle only those. Each type is written as a pointer ($$Type) and several are separated by commas. Any other error keeps propagating as usual:

try
    var y: any = $undeclared
except $$NameError
    $print("undeclared variable")
end

try
    -- code that may fail
except $$SyntaxError, $$ValueError
    $print("syntax or value problem")
end

Catching a general type also catches the more specific ones it groups: $$LookupError handles both IndexError and KeyError, for example.

Saving the Error (-> name)

Add -> name at the end of the header to save the error in a variable. It holds a string with the error type, where it happened, and the message:

try
    var y: any = $undeclared
catch -> e
    $print($e)   -- NameError: game.gbn:3:3: Variable not declared: undeclared
end

It combines with types:

except $$ValueError, $$TypeError -> e

Error Types

Every error shows its type at the start of the message instead of a generic Error::

NameError: game.gbn:12:3: Variable not declared: x
Type Raised when
NameError A variable, function, or class isn't declared
TypeError A value has the wrong type or an operation isn't valid for it
ValueError A value is not acceptable
SyntaxError The code is malformed (for example, a block without end)
IndexError An array index is out of range
KeyError A dictionary key doesn't exist
LookupError Groups IndexError and KeyError
GybinError Groups the three language-specific errors below
EventError An event is used incorrectly — for example, connecting something that isn't a function reference. Also handled by $$TypeError
PointerError A pointer is used incorrectly, or doesn't match its ptr[...] annotation. Also handled by $$TypeError
BitWidthError A value doesn't fit an int[N]/float[N], or a coercion mixes different widths (see §3). Also handled by $$ValueError
try
    var small: int[8] = 500
except $$BitWidthError -> e
    $print($e)
end

try
    $player_is_dead.connect(5)
except $$GybinError -> e
    $print("event, pointer or bit width problem: " + $e)
end

15. Pointers

The $$ operator creates a pointer to an existing variable, constant, function, class, or event (e.g., $$hp, $$Damage, $$self.hp, $$arr[0], $$player_is_dead). It is typed using ptr — the only type annotation that can hold a pointer value:

var hp: int = 100
var ref: ptr = $$hp

! Reading a pointer variable directly ($print(ref)) prints the pointer itself, not the value it references — use .value or .get() to read through it.

Pointer Methods and Properties

A pointer never carries a copy of what it points to — it resolves the target fresh every time one of these is accessed:

Member Description
.value Gets the current value of the referenced variable/constant. Can also be assigned ($ref.value = 75) to write through the pointer
.get() Same as .value, as a method call
.set(value) Same as assigning .value, as a method call
.call(args...) Invokes the referenced function/class, forwarding given arguments
.name The name of the referenced memory space
.is_callable true if the pointer references a function or class, false for a variable/constant
.is_mutable true only for a non-#reserved and non-constant variable
.size Approximate size in bytes of the referenced value
.ref The actual memory address (identity) of the referenced value
var hp: int = 100
var ref: ptr = $$hp

$print(ref.value)   -- 100
$ref.value = 75
$print(hp)           -- 75

func greet(name: str) -> NULL
    $print("Hello " + name)
end

var fp: ptr = $$greet
$fp.call("Carlos")   -- Hello Carlos

Rules

  • Only variables can be modified through a pointer. Constants are always immutable, and functions/classes cannot be assigned values — .set()/.value = ... throws an error in either case.
  • A variable declared with #reserved can never be mutated through a pointer, even by code holding a reference to it.
  • Writing through a pointer (.set()/.value = ...) accepts any value type; a mismatch against the target's declared type triggers a warning (with --w) instead of a strict type error.
  • Referencing a function or class purely by name (without $$) is a script error — $$name is the sole way to obtain a reference to one without invoking it.

Pointers enable indirect access and can target complex paths ($$object.field, $$array[0]). They are useful for aliasing, dynamic references, and passing function references (for instance, when attaching handlers to an event).

Typed Pointers: ptr[kinds][types]

A plain ptr accepts a pointer to anything. Two optional brackets narrow it down:

  • First bracket — what it can point to: var, const, event, func, class (separate several with commas).
  • Second bracket — the data type it must hold: int, str, a class name, etc. (separate several with commas).

An empty bracket, or any inside it, means no restriction on that part.

var p1: ptr[func] = $$greet                 -- only functions
var p2: ptr[var,const][int,float] = $$hp     -- a variable or constant holding an int or float
var p3: ptr[var][Fighter] = $$hero           -- a variable holding a Fighter instance
var p4: ptr[event] = $$player_is_dead        -- only events
var p5: ptr[] = $$hp                          -- same as a plain ptr
var p6: ptr[any][any] = $$hp                  -- same as a plain ptr

For classes, the second bracket says which class (or classes) it accepts: the named class itself or any class that extends it.

class Fighter
    var hp: int = 10
end

class Warrior extends Fighter
    var armor: int = 5
end

class Enemy
    var atk: int = 5
end

var a: ptr[class][Fighter] = $$Warrior          -- OK: Warrior extends Fighter
var b: ptr[class][Fighter,Enemy] = $$Enemy      -- OK
var c: ptr[class][Fighter] = $$Enemy            -- PointerError

A pointer that doesn't match its annotation raises a PointerError (see §14).

Raw Address Pointers

A ptr can also be constructed from a raw integer address instead of $$target:

var memory: ptr = 0x7fff5fbff80c

! This does not dereference actual process memory — doing so safely from an AST-walking interpreter is impossible (there is no way to know if a given address is even valid, and attempting to do so risks crashing the process or reading unowned memory). What you obtain instead is an opaque handle storing the address as pure identity:

$print($memory.ref)          -- the address itself
$print($memory.name)         -- "0x7fff5fbff80c"
$print($memory.is_callable)  -- false
$print($memory.is_mutable)   -- false
$print($memory.size)         -- native pointer size (8 bytes)

$print($memory.value)        -- error: nothing to dereference
$memory.value = 5             -- error: nowhere to write
$memory.call()                -- error: nothing to call

Same object, same member surface as a standard pointer — .value, .set(), and .call() simply have nothing to act on, and state so clearly rather than guessing.


16. Events

An event declares a lightweight signal: a name plus a parameter list:

event player_is_dead(entity: str)

Type annotations on parameters serve strictly as documentation — an event has no body against which to validate them.

Methods

Every event exposes the following, all used with the $ prefix like any other call:

Member Description
.connect(handler) Registers a function to execute whenever the event fires. handler must be a pointer to a function created with $$function_name; the bare name, or a pointer to anything else (a variable, class, event...), causes an EventError
.disconnect(handler) Removes that handler from the event. Does nothing if it wasn't connected
.reconnect(handler) Disconnects and connects the handler again, so it ends up connected exactly once — useful to avoid running it twice on each .emit()
.emit(args...) Calls each connected handler in the order they were attached, forwarding provided arguments. Argument count must match the parameter count declared by the event
.last_connection Property (no parentheses): a pointer to the most recently connected handler, or NULL if none is connected
event player_is_dead(entity: str)

func on_player_dead(entity: str) -> NULL
    $print(entity + " died")
end

$player_is_dead.connect($$on_player_dead)

if $health <= 0
    $player_is_dead.emit("Zombie")
end

Multiple handlers can be connected to the same event; all will execute in the order attached when .emit(...) is called.

.disconnect(...) takes any pointer to the same function, so you don't need to keep the one used to connect:

$player_is_dead.disconnect($$on_player_dead)
$player_is_dead.connect($$on_player_dead)
$player_is_dead.connect($$on_player_dead)     -- connected twice: runs twice per emit
$player_is_dead.reconnect($$on_player_dead)   -- now connected exactly once

$print($player_is_dead.last_connection.name)  -- on_player_dead

Events as Conditions

An event can be used directly as a condition: it is false until the first time it is emitted and true from then on.

event level_loaded(name: str)

func init() -> NULL
    if $level_loaded
        $print("not printed yet")
    end

    $level_loaded.emit("Forest")

    if $level_loaded
        $print("printed: the event has been emitted")
    end
end

17. Modules and Imports

@use — Importing a Module

Loads a .gbn file (or other supported format) and exposes all its symbols in the current scope:

@use "utils.gbn"
@use "helpers"        -- detects extension automatically

Importing is idempotent: if a module was already loaded, it is not re-executed.

Importing Multiple Modules at Once

@use math, random, sys

Each loads independently, exactly as if on its own @use line. @as is not allowed on @use with multiple modules — a single alias cannot represent multiple distinct modules — import them one per line if each requires its own name:

@use math @as math_lib
@use random @as rand_lib

@from / @as — Importing with Aliases

Loads a module and exposes it under a named namespace:

@from "utils.gbn" @as utils
$print($utils.my_function())

@use path @as alias (single module) accomplishes the exact same thing — identical functionality reached via either keyword.

Selective Imports (@from ... @use)

Loading an entire module to use only one or two functions wastes memory on everything else defined within it, and can fill --w output with warnings about symbols the importer never requested. @from module @use $$symbol loads only named symbols instead of executing the whole module and pulling in everything it exposes:

@from math @use $$_sqrt
$print($_sqrt(4.0))

Multiple symbols from the same module, separated by commas:

@from math @use $$_PI, $$_E, $$_sqrt

Add @as to group selected symbols under a single namespace rather than dumping them directly into current scope — permitted here (unlike multi-module @use above) because everything still originates from a single module:

@from sys @use $$_format_time, $$_miliseconds @as os
$print($os._format_time())

#reserved symbols still cannot be imported this way, selectively or otherwise.

Supported Formats

Extension Behavior
.gbn Executed and integrated into the current scope
.py Loaded as a Python module; public attributes exposed
.c, .cpp, .asm, .sh, .bash, .h Source code exposed as a dictionary under __source__

Automatic Module Search

If the path contains no / and does not start with ., the interpreter also searches in the project's libs/ directory.


18. Memory Management

The interpreter features a configurable default limit of 1024 memory slots.

free — Freeing a data space

Explicitly removes a variable from scope:

$free($$my_data) 

free accepts one or several pointers of any kind (variables, constants, functions, classes, events), separated by commas:

$free($$first, $$second, $$third)

expand_memory — Expanding the Limit

Increases the maximum number of available slots:

$expand_memory(512)

! This outputs a warning to stderr indicating the change.

breakpoint — Pausing Execution

Pauses execution and returns a memory summary:

$breakpoint()

Expression Caching

Expressions appearing repeatedly (e.g., inside a loop) are cached as AST upon first parse, avoiding redundant re-parsing during each iteration:

var i: int = 0
while $i < 10000
    var x = ($i * 5) + (20 / 2)  -- expression is cached
    $i += 1
end

Caching and --nch

Two features store results to save work on repeated use:

  • #onready func remembers a function's result for each distinct set of arguments (see §7).
  • $preload(path) reads a file once and remembers its content (see §19).

Run with --nch to turn both off for that execution: cached functions always run their body, and $preload always reads from disk.

Gybin my_script.gbn --nch

Post-execution Garbage Collector

Upon program completion, the engine automatically frees global symbols defined but never read (global variables, functions, classes, imported namespaces). $free($var) can also be used manually if needed.

--sm — Memory Summary

When using the --sm flag, the interpreter prints full memory status on termination:

Total memory: hp:int=100 | name:str='Juan' | ...

19. Native Functions (built-ins)

These functions are available without importing anything:

Function Description
$print(value) Prints a value
$reprint(value) Like $print, but overwrites the current line instead of starting a new one — see below
$int(value) Converts to integer
$float(value) Converts to float
$str(value) Converts to string
$bool(value) Converts to boolean
$range(n) Generates a range from 0 to n-1
$len(collection) Returns collection length
$file_read(path) Reads an entire file as a string
$file_lines(path) Reads a file as an array of lines
$file_write(path, content) Writes (overwrites) a file
$file_append(path, content) Appends content to the end of a file
$file_exists(path) Returns true if the file exists
$preload(path) Reads a file like $file_read, but remembers its content — see below

Named Arguments in $print

$print accepts sep, end, and flush by name:

$print("a", "b", sep="-", end="")   -- a-b, no trailing newline
$print("c")

$preload — Reading a File Once

$preload(path) returns the whole content of a file as a string, just like $file_read(path). The difference is that it remembers the result for that path: later calls with the same path return the stored content instantly without touching the disk again, even if the file changed in the meantime.

var config: str = $preload("config.txt")   -- reads the file
var again: str = $preload("config.txt")    -- returns the stored content

Run with --nch to make every $preload read from disk.

$reprint — Updating a Line in Place

Status and progress outputs typically need to overwrite the same terminal line rather than pushing down a new line on every call. $reprint achieves this without requiring manual and ANSI clear sequence handling:

$reprint("loading 10%")
$reprint("loading 100%")
$reprint("done!", end="
")   -- pass end="
" to close the line and return to normal $print
$print("after")

By default, it adds no trailing newline (so subsequent calls overwrite) and flushes immediately. Passing end=" " makes it behave identically to $print.


20. Standard Library (stdutils)

stdutils.gbn is automatically loaded at startup for every program. It provides:

Version Constant

$print($GBN.VERSION) 

Collection Functions

Function Description
$contains(collection, element) true if element exists in collection
$index_of(collection, element) Index of element, or -1 if missing
$has_key(dict, key) true if key exists in dictionary
$merge_dict(dest, source) Merges source into dest and returns result

String Functions

Function Description
$str_split(text, delimiter) Splits string into an array
$str_join(separator, items) Joins an array of strings into one
$to_lower(text) Converts string to lowercase
$to_upper(text) Converts string to uppercase
$starts_with(text, prefix) true if text starts with prefix
$ends_with(text, suffix) true if text ends with suffix

Vector Classes

Function Description
$vec2(x, y) Vector of 2 float values
$vec2i(x, y) Vector of 2 int values
$vec3(x, y, z) Vector of 3 float values
$vec3i(x, y, z) Vector of 3 int values
$color(r, g, b, a) Vector of 4 float values (0 to 1 range), where a defaults to 1 if unassigned

You can access vector components at any time via .x, .y, .z or .r, .g, .b, .a.

Value Clamping

The first argument of $max() or $min() is the target value, and the second is the boundary. If the target exceeds the boundary limit, the boundary value is automatically returned:

var min_val: int = $min(25, 12) -- Minimum possible value is 12
var max_val: int = $max(72, 100) -- Maximum possible value is 100

User Input

var name: str[64] = $input("What is your name? ")

Pause for Input

$pause() -- Code execution stops here until key press

21. Compilation to Executable

--c/--fc generate a standalone executable using PyInstaller. Neither flag executes your script for display — the only output printed is the path of the resulting executable or a message explaining why compilation was skipped.

Gybin my_script.gbn --c

The interpreter lacks a dedicated "syntax-check only" mode — it is an AST-walking engine, meaning error checking and script execution are technically the same operation. --c still verifies script validity, running it once internally with all output (stdout/stderr) completely muted — none of its output or side effects leak. --fc skips even that: it never touches script execution and compiles unconditionally.

Flag Executes Script? Compiles on Error?
--c Completely muted (error checking only) No - prints Compilation skipped... and exits
--fc Never Yes, always

--n, --ad, --i

Gybin game.gbn --c --n MyGame --ad assets/config.json --i icon.ico
  • --n NAME — custom name for compiled executable (defaults to script filename without extension).
  • --ad PATH[=DEST] — packages an extra file/folder into executable. Can be repeated. DEST is target destination folder inside package (defaults to .).
  • --i ICON_PATH — executable icon (.ico on Windows, .icns on macOS).

Automatic Import Packaging

Every file imported via @use or @from ... @as — at any depth level (including transitive imports) and of any supported extension — is automatically detected and embedded in the binary. The compiled program becomes completely self-contained, requiring no original .gbn files to run.

stdutils.gbn is also embedded directly inside the binary and is never copied alongside as an external file — this is intentional: as it auto-loads on every execution, an editable external copy would pose a code injection security risk for a compiled binary.

If PyInstaller is unavailable, a bash wrapper (.bat on Windows) is generated instead:

#!/usr/bin/env bash
exec Gybin "my_script.gbn" "$@"

! A script whose own filename contains : compiles fine as an entry point. However, imported files containing : in their filename cannot be bundled, as PyInstaller's --add-data uses : as a SOURCE/DEST separator. You will receive a clear warning during compilation if this occurs, and only that file will be skipped rather than failing the entire compilation process.


22. Warnings and Static Analysis

When executing with --w, the engine activates post-execution static analysis reporting:

  • Declared variables that are never read
  • Defined functions that are never called
  • Functions with empty bodies
  • Defined classes that are never instantiated
  • Classes with empty bodies
  • Defined enums that are never used
  • Multi-type variables with more than 3 distinct types assigned
  • Suspicious assignments (declared type differs from actual value type)
  • Potential memory leaks: global containers holding over 256 unread elements

Warnings output to stderr following this format:

Warning: file.gbn:42: Variable 'x' is declared but never read

Variables prefixed with _ are ignored by the static analyzer (by convention representing intentionally unused items).

Warnings may occasionally flag code you did not write if it belongs to unused routines within imported libraries.


23. Rules and Best Practices

The $ Operator

$ is mandatory for:

  • Reading variable values: $hp
  • Calling functions: $print(...), $add(1, 2)
  • Instantiating classes: $Player("Juan")
  • Assigning to an existing variable: $hp = 50
  • Chained member calls: $p.damage(30)

$ is not used in:

  • Initial declarations (var hp: int = 100)
  • Accessing object fields following a dot ($p.hp, not $p.$hp)
  • Parameter names in function signatures

Scope

Variables declared inside a block (function, loop, conditional) are local to that block. Shadowing an outer variable inside an inner scope can cause ambiguity conflicts:

var global_val: int = 100

func test() -> NULL
    var global_val: int = 50  -- local redefinition
    $print($global_val)       -- error: scope ambiguity
end

It is recommended to use distinct names for local variables coexisting with global variables of similar purpose.

Use of any

any disables type checking. Use it strictly when necessary, such as for variables receiving values of unknown type at declaration time.

Collection Initialization

Always initialize arrays and dicts with [] or {} if you intend to manipulate them immediately. Using NULL initially and attempting to append items without first instantiating a container produces an error.

-- Correct:
var items: array[Item] = []
$items.append($Item("Sword"))

-- Problematic if appending before assignment:
var items: array[Item] = NULL
$items.append($Item("Sword"))  -- error

Closing Blocks with end

Every block structure (func, class, if, while, for, try, match) must terminate with end. Unclosed blocks raise a SyntaxError.

! While writing code outside functions is supported, organizing most of your script logic into functions is strongly recommended.

Indentation

! Indentation is not strictly required by the parser; however, keeping consistent indentation is recommended for readability. Furthermore, stripping indentation can marginally benefit performance in large scale codebases.

About

A language for experimenting with explicit sintax and control memory

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages