Varse

Documentation

Everything you need to write programs in Varse. Back to home

Installation

Varse is a single executable with no external dependencies. Build from source with NASM and GoLink:

git clone https://github.com/varse-lang/varse
cd varse
nasm -f win64 varse.asm -o varse.obj
golink /entry:main /console varse.obj kernel32.dll user32.dll gdi32.dll gdiplus.dll wininet.dll

Cloning gets you the interpreter source, the build script, the assembler and linker in tools/, and the examples. The interactive console and every GUI widget live in the kernel32, user32, gdi32, gdiplus and wininet libraries, so there is nothing else to install.

Hello World

Create a file called hello.var:

fn main() {
    print(42);
}
42

Running Code

Every Varse program must have a fn main() function. The interpreter parses all definitions first, then finds and executes main.

varse hello.var

Standalone executables

Pass --build and Varse copies the interpreter, appends the source to it, and writes the result out. The .exe that comes out carries its own source, so it runs on its own with nothing else installed:

varse --build hello.var -o hello.exe
hello.exe

The built program reads the source from its own tail at startup, so it keeps running the code exactly as it was when you built it — rebuild after editing the .var.

Built-ins Quick Reference

The entire language is 7 built-in functions and 26 statement keywords, plus 2 command line forms. That is the whole surface — anything else is defined by your own program. Both tables below link to the section that documents each one.

Built-in functions (7) — the only names always in scope

These seven are resolved before any of your own definitions, so they cannot be shadowed. Write one bare (netlength()) or as part of a larger expression (setnum status netstatus();). print() looks like a call too but starts a statement, so it is listed below.

FunctionReturnsMore
netstatus()HTTP status code of the last request (200, 404, 0 on failure)netstatus()
netlength()Size of the stored response body in bytesnetlength()
cssbg()Page background color as a numbercssbg()
cssfg()Page text color as a number, 0 if the page sets nonecssfg()
htmlblocks()How many blocks the stored page hashtmlblocks()
blocklen(i)Characters in block iblocklen()
blocktag(i)Element kind of block i (1–6 heading, 7 li, 8 pre, 9 quote, 10 table, 0 other)blocktag()

Statements (26 keywords, plus assignment) — used on their own line

Each of these starts a statement. The first seven are the core language; the rest are the GUI, networking and page readers. Assignment (name = expr;) is not a keyword — a bare name followed by = updates an existing variable.

StatementWhat it doesMore
print(expr);Writes a number to the consoleprint()
let name = expr;Declares a variableVariables
name = expr;Assigns to an existing variableVariables
def name: value;Declares an immutable constantConstants
fn name(params) { }Defines a functionFunctions
return expr;Returns from a functionReturn Values
if (cond) { } else { }Conditional executionIf / Else
prints "text";Writes a string to the consoleprints
window "t" w h bg=#HEX;Creates the windowwindow
button name x y w h "t" ...;Creates a clickable buttonbutton
label name x y w h "t" ...;Creates a text labellabel
input name x y w h "t" ...;Creates an editable text fieldinput
panel name x y w h ...;Creates a filled surfacepanel
onclick name { };Runs a block when a button is clickedonclick
show;Enters the message loopshow
settext w "text";Replaces a widget's text with a stringsettext
setnum w expr;Replaces a widget's text with a numbersetnum
setcolor w expr;Changes a widget's colorsetcolor
sethide w expr;Hides or shows a widgetsethide
resetui n;Drops widgets past n before rebuilding a layoutresetui
httpget "url";HTTP GET a URL or an input widget's texthttpget
netprint;Writes the stored body to the consolenetprint
netsave "path";Writes the stored body to a filenetsave
nettext w;Puts the stored body into a widgetnettext
htmlstrip w;Puts the page's readable text into a labelhtmlstrip
htmltitle w;Puts the page's title into a labelhtmltitle
htmlblock w i;Puts block i of the page into a labelhtmlblock

Command line (2)

CommandWhat it doesMore
varse file.varRuns a programRunning Code
varse --build file.var -o out.exeWrites a standalone exe with the source embeddedStandalone executables

Not in the language at all

MissingWhat to do instead
Loops (while, for)Recursion, or unroll by hand — No loops
Strings as valuesUse the engine's readers — Strings are not values
Arrays / listsNot available
Modulo, bit shifts, logic operatorsOnly + - * / and the comparisons exist — Operators
HTTP POST, headers, JSONhttpget is GET only; parse the body yourself as text
Fonts, sizes, scrollingNot available — headings differ by color and spacing only — Notes & Limits

Variables

Variables store values that can be changed. Declared with let.

let name = expression;
fn main() {
    let x = 10;
    let y = x + 5;
    print(y);
    x = 20;
    print(x);
}
15 20

Constants

Constants are immutable values declared with def. They are resolved at the start of execution.

def name: value;
def pi: 314;
def max_retries: 3;

fn main() {
    print(pi);
}
314

Functions

Functions are declared with fn and contain a block of statements. Functions can be nested.

fn name(params) {
    statements
}
fn add(a + b) {
    return a + b;
}

fn main() {
    print(add(10 + 20));
}
30

Parameters

Parameters are separated by + (or ,) in both the definition and the call. When calling a function, + acts as the argument separator. To pass an arithmetic expression as a single argument, wrap it in parentheses.

SyntaxDescription
fn(a + b)Two parameters
fn(x)One parameter
fn()No parameters
add(10 + 20)Call with 2 arguments (10 and 20)
add((x + y))Call with 1 argument (the expression x + y)

Return Values

Use return to send a value back from a function.

fn fib(n) {
    if (n <= 1) {
        return n;
    }
    return fib(n - 1) + fib(n - 2);
}

fn main() {
    print(fib(10));
}
55

Operators

Arithmetic

OpDescriptionExample
+Addition / parameter separatorx + y
-Subtraction / unary negationx - y, -x
*Multiplicationx * y
/Integer divisionx / y

Comparison

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

Precedence (high to low)

LevelOperators
1Unary -
2*, /
3+, -
4<, >, <=, >=, ==, !=

Comments

// This is a single-line comment

/*
   This is a
   block comment
*/

If / Else

Conditional execution with if, else if, and else. The condition is always wrapped in its own parentheses, even when it is a bare comparison.

fn classify(n) {
    if (n < 10) {
        return 1;
    } else if (n < 100) {
        return 2;
    } else {
        return 3;
    }
}

fn main() {
    print(classify(4));
    print(classify(42));
    print(classify(999));
}
1 2 3

Blocks & Scope

Variables declared inside a function are scoped to that function. Nested functions can access variables from enclosing scopes.

def base: 10;

fn main() {
    fn add(x + y) {
        return x + y;
    }
    let sum = add(base + 2);
    print(sum);
}
12

print()

Built-in function that outputs a number to the console.

ParameterTypeDescription
expressionnumberThe value to output. Any expression that evaluates to a number.

Multiple print() calls concatenate on the same line with no separator.

fn main() {
    print(197);
    print(12345);
}
19712345

prints

Like print(), but writes a string literal instead of a number. Escape sequences are processed inside the literal.

prints "text";
EscapeOutput
\nNewline
\tTab
\\Backslash
\"Double quote
fn main() {
    prints "hello ";
    print(42);
    prints "\n";
}
hello 42

GUI Framework

Varse includes a built-in GUI system for creating native Windows applications with styled widgets, rounded corners, and click handlers. The GUI uses Win32 API and GDI under the hood.

A GUI program defines widgets, registers click handlers, then calls show to enter the message loop.

Geometry

Every widget takes x, y, width and height, and each of the four is a full expression evaluated as the widget is created. That is what makes a flow layout possible: a widget can sit at a running y variable, and the next one below it at y + height.

let y = 120;
label row1 24 y 600 30 "first";
y = y + 38;
label row2 24 y 600 30 "second";

A name followed directly by ( is read as a call, so write a parenthesised expression with no space before the bracket, and keep a leading bracket away from a variable: x (w - 8) would parse as a call to x.

Colors & options

Every widget accepts the same optional trailing options, written after the text and in any order.

OptionValueApplies to
color=A #RRGGBB literal, or any expression that returns a color such as cssbg()button and input and panel fill, label text
radius=Corner radius in pixels. Has to be a literal number; an expression here is a syntax errorbutton, input, panel
bg=Window background color, same form as color=window only

Defaults are per widget: a button is #CC4422 with radius 8, an input is #303033 with radius 6, a panel is white and unrounded, a label's text is black, and a window's background is white.

button go 752 20 130 42 "Go" radius=8 color=#2864C7;
label content 44 144 972 90 "" color=cssfg();   // colour from an expression

A color is an ordinary number, so it can be held in a variable and passed to setcolor like any other value. An option the engine does not recognise is parsed and skipped, so a misspelt option name fails quietly instead of pointing at the mistake.

window

Creates the application window. Must be the first GUI statement.

window "title" width height bg=#HEX;
ParameterTypeDescription
titlestringWindow title bar text
widthnumberWidth in pixels
heightnumberHeight in pixels
bgcolorBackground color #RRGGBB or an expression (optional, default #FFFFFF)
window "My App" 800 600 bg=#121212;

button

Creates a clickable button with optional rounded corners and color.

button name x y w h "text" radius=N color=#HEX;
ParameterTypeDescription
nameidentifierUnique name for onclick
x, ynumberPosition from top-left
w, hnumberWidth and height in pixels
textstringLabel text, centered
radiusnumberCorner radius, default 8 (optional)
colorcolorFill color #RRGGBB or an expression (optional, default #CC4422)

Visual defaults for the widget, on the dark theme the examples use:

button go 20 20 120 40 "Go";          // #CC4422, radius 8
button go 20 20 120 40 "Go" radius=0 color=#2864C7;   // square and blue

label

Creates a text label. Not clickable.

label name x y w h "text" color=#HEX;
ParameterTypeDescription
nameidentifierUnique name
x, ynumberPosition from top-left
w, hnumberText bounding box
textstringText to display
colorhexText color #RRGGBB (optional, default #000000)

input

Creates a single-line text field. Clicking it gives it keyboard focus; typed characters are appended to its text. Its contents can be read by httpget and written with settext.

input name x y w h "default" radius=N color=#HEX;
ParameterTypeDescription
nameidentifierUnique name
x, ynumberPosition from top-left
w, hnumberWidth and height in pixels
defaultstringInitial contents (optional)
radiusnumberCorner radius, default 6 (optional)
colorcolorFill color #RRGGBB or an expression (optional, default #303033)
input address 20 20 700 42 "https://example.com" radius=8 color=#26262B;

panel

Creates a filled rectangle used as a surface behind other widgets — a page area, a card, or a colored strip. Panels are not clickable and draw no text.

panel name x y w h radius=N color=#HEX;
ParameterTypeDescription
nameidentifierUnique name
x, ynumberPosition from top-left
w, hnumberWidth and height in pixels
radiusnumberCorner radius, default 0 (optional)
colorcolorFill color #RRGGBB or an expression (optional, default #FFFFFF)
panel page 20 78 1020 600 radius=6 color=#FFFFFF;

Widgets are drawn in declaration order, so a panel declared before a label sits behind it. Labels wrap text to their width and honour newlines.

show

Creates the window and enters the message loop. Blocks until the window is closed.

show;

onclick

Registers a callback that runs when a button is clicked.

onclick button_name {
    statements
};
window "Clicker" 400 200 bg=#222222;

button btn1 20 20 160 50 "Print 42" radius=25 color=#1DB954;

onclick btn1 {
    print(42);
};

show;

A handler is a normal block, so variables declared with let in main stay visible inside it and can be mutated. Pair that with the widget update statements below to make an interactive program.

Widget Updates

Widgets are addressed by the name they were created with. These statements can appear anywhere, including inside an onclick handler. Each one repaints the window right away.

settext

Replaces the text of a button, label or input with a string literal.

settext widget "text";
ParameterTypeDescription
widgetidentifierThe name the widget was created with
textstringNew text. Has to be a literal — a string cannot be held in a variable
settext status "Saved";

setnum

Replaces the text of a button, label or input with the result of a numeric expression. Numbers are turned into decimal text for you, so this is the usual way to show a value that changes.

setnum widget expression;
ParameterTypeDescription
widgetidentifierThe name the widget was created with
expressionnumberAny numeric expression, including function and built-in calls
setnum score netstatus();

setcolor

Changes a widget's color. On a button, input or panel it is the fill color; on a label it is the text color.

setcolor widget expression;
ParameterTypeDescription
widgetidentifierThe name the widget was created with
expressioncolorA #RRGGBB literal or anything that returns a color, such as cssbg()
setcolor row #3C7A3C;
setcolor page cssbg();

sethide

Shows or hides a widget. Any non zero value hides it, 0 shows it. Hidden widgets are skipped when drawing and when testing clicks, so they cannot be clicked, and hiding a widget is how you drop a widget you have no data for instead of rebuilding the layout.

sethide widget expression;
ParameterTypeDescription
widgetidentifierThe name the widget was created with
expressionnumberNon zero hides, 0 shows
fn taghide(i) {
    if (blocktag(i) == 7) {   // a list item
        return 0;          // keep the bullet
    }
    return 1;              // hide it everywhere else
}

sethide bullet taghide(3);

Widget statements can appear anywhere a statement can, including inside an onclick handler, and each one repaints the window immediately.

let n = 0;

onclick inc {
    n = n + 1;
    setnum value n;
    if (n >= 10) {
        settext value "max";
        setcolor value #CC4422;
    }
};

Networking

Varse makes HTTP requests through the WinINet API. The response body is kept in a buffer that stays readable until the next request.

httpget

Performs an HTTP GET request and stores the response body. The body stays in the buffer until the next request, so read it before you fetch anything else.

httpget "https://example.com";
httpget address;
ParameterTypeDescription
urlstring or identifierA URL literal, or the name of an input widget — the widget's current text is then used as the URL, which is how an address bar is wired up
httpget "https://example.com";
print(netstatus());          // 200
print(netlength());         // 559

netprint

Writes the stored response body to the console, unchanged and without a trailing newline. Handy for looking at what a page actually returned when a renderer shows nothing.

netprint;
ParameterTypeDescription
——Takes no arguments; it always prints the current buffer
httpget address;
netprint;
prints "\n";

netsave

Writes the stored response body to a file, overwriting it if it already exists. The path is relative to the directory the program was started in.

netsave "out.html";
ParameterTypeDescription
pathstringFile to write, created or replaced
httpget "https://example.com";
netsave "page.html";

nettext

Sets the text of a button or label to the stored response body, which puts a result straight into the UI without going through the console. The body arrives as it was sent, tags and all — use htmlstrip or htmlblock for a page.

nettext widget;
ParameterTypeDescription
widgetidentifierLabel or button to receive the body
httpget "https://example.com";
nettext raw;          // raw is a label

netstatus()

Built-in function that returns the HTTP status code of the last request. It is filled in by httpget, so read it after the request returns. On a failed request it is 0.

netstatus()
ReturnsTypeDescription
codenumber200 on success, 404 for a missing page, 0 when the request never completed
setnum status netstatus();
if (netstatus() == 0) {
    settext status "request failed";
}

netlength()

Built-in function that returns the number of bytes in the stored response body — the size the server sent, useful as a sanity check that something arrived.

netlength()
ReturnsTypeDescription
bytesnumberBody size in bytes, 0 when the buffer is empty
prints "loaded ";
print(netlength());
prints " bytes\n";
fn main() {
    httpget "https://example.com";
    print(netstatus());
    prints " ";
    print(netlength());
    netsave "page.html";
}
200 559

HTML & CSS

Fetched pages can be turned into plain text and their stylesheet colors extracted. Both readers work on the body stored by the last httpget.

htmlstrip

Extracts the readable text of the stored page into a label, dropping script and style blocks, unescaping entities such as &amp; and &nbsp;, collapsing runs of spaces, and inserting newlines at block boundaries. Labels wrap text to their width, so the result renders as a paragraph.

htmlstrip widget;
ParameterTypeDescription
widgetidentifierLabel to write the extracted text into

For a page of <h1>Example Domain</h1><p>This domain is for use in examples.</p> the label ends up holding two lines:

Example Domain
This domain is for use in examples.

htmltitle

Copies the contents of the page's <title> element into a label. The label is left untouched if the page has no title.

htmltitle widget;
ParameterTypeDescription
widgetidentifierLabel to write the title into
httpget "https://example.com";
htmltitle title;      // title now reads "Example Domain"

cssbg()

Returns the background color the page sets, as a color value usable by setcolor. It recognises both background and background-color, named colors, and three and six digit hex shorthand, so example.com's body{background:#eee} yields #EEEEEE.

cssbg()
ReturnsTypeDescription
colornumberThe page's background color, or white when no rule is found
setcolor page cssbg();

cssfg()

Returns the page's text color, taken from the first color rule in the page's stylesheet. Unlike cssbg() there is no sensible fallback, so a page that sets no color gives 0 and the caller picks its own default.

cssfg()
ReturnsTypeDescription
colornumberThe page's first color rule as a color value, or 0 when the page sets none
if (cssfg() != 0) {
    setcolor content cssfg();
}

Putting it together

httpget "https://example.com";
htmltitle title;
htmlstrip content;
setcolor page cssbg();
setcolor content cssfg();

htmlblock

Halfway between a text dump and a layout engine: copies a single top level block of the stored page into a label. Blocks are split at block level elements — div, p, h1–h6, ul, ol, li, pre, blockquote, table, tr, td, header, footer, section, article, nav, form and hr. Inline markup such as <a>, <b> or <span> stays inside its block, and <script>, <style>, <head>, <svg>, <iframe> and <template> are skipped entirely. Entities are unescaped and runs of whitespace collapse, exactly as in htmlstrip.

htmlblock widget index;
ParameterTypeDescription
widgetidentifierLabel to write the block's text into
indexexpressionZero based block number. An index past the end clears the widget

htmlblocks()

Returns how many blocks the stored page has, so a layout can stop at the last one.

htmlblocks()
ReturnsTypeDescription
countnumberNumber of top level blocks, 0 when nothing has been fetched
let n = htmlblocks();   // 3 for example.com

blocklen()

Returns the number of characters in a block. Since a label wraps its text, this is what tells the layout how many lines a block will need and therefore how tall to make it.

blocklen(index)
ParameterTypeDescription
indexnumberZero based block number
ReturnsTypeDescription
lengthnumberCharacters in the block, 0 when the index is out of range
let lines = blocklen(0) / 120 + 1;
let height = lines * 17 + 14;

blocktag()

Returns a number for the element that opened the block, which is how a rendered page knows a heading from a paragraph from a list item:

ValueElement
1 – 6h1 – h6
7li
8pre
9blockquote
10table, tr, td, th
0Everything else, including plain text
if (blocktag(3) == 7) {
    setcolor row #3C7A3C;
}

resetui

Forgets every widget past the first n and starts the list over. A page that rebuilds its own layout — a browser navigating to a new page — calls this first, otherwise every render would stack another copy of the page on top of the last, until the widget limit is hit.

Keep the widgets that are not part of the rebuilt layout. A window plus an address bar, a Go button and a page label is resetui 4;: the rendered page goes, the chrome stays. Keeping the chrome matters for input — rebuilding a widget resets it to its declared default text, so an address bar recreated on every navigation would always hold the URL it was declared with. With no count at all, everything except the window goes.

resetui n;
ParameterTypeDescription
nexpressionHow many widgets to keep, counted from the window. Optional, defaults to 1

Rendering a page

A renderer unrolls a pool of slots and guards each one with the block count, because widget geometry is fixed when the widget is created. Each slot measures its block, creates the label, asks the engine for the text, then moves the cursor down:

resetui 4;          // window, address, go, page - keep the chrome
let y = 148;
let n = htmlblocks();
let h = 0;

if (n > 0) {
    h = blkh(0);
    label t0 24 y 1034 h "" color=#B9B9BD;
    htmlblock t0 0;
    setcolor t0 blkcol(0);
    y = y + h;
}

if (n > 1) {
    h = blkh(1);
    label t1 24 y 1034 h "" color=#B9B9BD;
    htmlblock t1 1;
    setcolor t1 blkcol(1);
    y = y + h;
}

Each height comes from a helper that reads the block length and tag:

fn blkh(i) {
    let t = blocktag(i);
    let l = blocklen(i);
    let lines = l / 120 + 1;
    if (t > 0) {
        if (t < 7) {
            return 32;
        }
    }
    let h = lines * 17 + 14;
    if (h < 28) {
        h = 28;
    }
    return h;
}

Notes & Limits

Varse is small on purpose. A few things are worth knowing before you lean on it.

No loops

There is no while or for. Repetition is either written out or done with recursion — fib calls itself. That is also why a layout needing one widget per item, such as the browser's page slots, is unrolled by hand rather than generated in a loop.

Strings are not values

A string literal is only ever handed to a statement — a widget's text, settext, prints, httpget, netsave. It cannot be stored in a variable, joined with another, or compared, so anything text shaped has to come through the engine's readers: htmlstrip, htmlblock, htmltitle and the block accessors. That boundary is why the browser is a layout engine for text the engine already knows how to find, rather than a parser written in Varse.

Numbers

Arithmetic is integer arithmetic and division truncates, so l / 120 + 1 is how a block length becomes a line count. Colors are numbers as well, written #RRGGBB, and print() prints numbers.

Widget budget

A program can hold 128 widgets, and creating one past that limit is fatal rather than silently ignored. The browser is nowhere near it: it spends 10 on its chrome and two per rendered block (a bullet and a line of text), so 40 slots come to 90.

What actually stops the browser at 40 blocks is its own guard, not the widget cap. Each slot checks the block count and, once the running y passes the bottom of the page surface, sets a flag that switches off every later slot — so a long page is cut off rather than scrolled, and the page never overflows into the chrome below it. Raising the slot count means adding another guarded block to the source, which is why it is unrolled by hand instead of generated.

Example: Browser Engine

An address bar, a Go button, and a page surface. The initial page loads on startup; Go re-reads the address field, so anything you type is fetched and rendered. The listing below is the minimal version — one label holding the whole stripped page.

fn main() {
    window "Varse Browser" 1060 760 bg=#17171B;

    input address 20 20 716 42 "https://example.com" radius=8 color=#26262B;
    button go 752 20 130 42 "Go" radius=8 color=#2864C7;

    panel page 20 78 1020 600 radius=6 color=#FFFFFF;
    label title 44 100 972 34 "" color=#111111;
    label content 44 144 972 518 "" color=#222222;
    label status 20 692 1020 26 "Type a URL and press Go" color=#8A8A93;

    httpget "https://example.com";
    htmltitle title;
    htmlstrip content;
    setcolor page cssbg();
    setcolor title cssfg();
    setcolor content cssfg();
    setnum status netstatus();

    onclick go {
        httpget address;
        htmltitle title;
        htmlstrip content;
        setcolor page cssbg();
        setcolor title cssfg();
        setcolor content cssfg();
        setnum status netstatus();
    };

    show;
}

Clicking the address field focuses it and clears the default text on the first keystroke.

The shipped examples/bengine.var goes further and lays the page out the way a browser does. The window, address bar and buttons are declared once in main; render() then rebuilds only the page, which is why resetui keeps the chrome and drops the old page. Inside it, htmlblocks() gives the number of blocks, each one is measured with blocklen() and styled from blocktag(), and htmlblock fills the slot. Headings come out black on a light page and white on a dark one, list items get an indent and a bullet, and the page's own stylesheet colours the surface and the text through cssbg() and cssfg(). Clicking Go re-reads the address bar, so a URL you type is the URL that loads.

Example: Fibonacci

fn fib(n) {
    if (n <= 1) {
        return 1;
    }
    return fib(n - 1) + fib(n - 2);
}

fn classify(n) {
    if (n < 10) { return 1; }
    else if (n < 100) { return 2; }
    else { return 3; }
}

fn main() {
    let z = fib(10);
    print(z);
    print(classify(4));
    print(classify(10));
    print(classify(99));
}
891234

Example: GUI App

window "Varse GUI" 600 400 bg=#222222;

label title 20 20 300 40 "Hello from Varse!" color=#FFFFFF;

button btn1 20 130 180 50 "Print Number" radius=10 color=#CC4422;
button btn2 220 130 180 50 "Say Hello" radius=10 color=#226644;
button btn3 20 200 180 50 "Big Radius" radius=25 color=#4444CC;

onclick btn1 { print(42); };
onclick btn2 { print(100); };
onclick btn3 { print(999); };

show;

Example: Counter

A complete interactive app. The handler mutates n, then pushes the new value and a new color into the label.

fn main() {
    window "Counter" 520 320 bg=#111111;

    label heading 30 25 460 40 "Counter" color=#FFFFFF;
    label value 30 80 460 60 "0" color=#1DB954;

    button inc 30 170 150 50 "Add One" radius=10 color=#2864C7;
    button dec 190 170 150 50 "Minus One" radius=10 color=#444444;
    button reset 350 170 150 50 "Reset" radius=10 color=#774422;

    let n = 0;

    onclick inc {
        n = n + 1;
        setnum value n;
    };

    onclick dec {
        n = n - 1;
        setnum value n;
        setcolor value #CC4444;
    };

    onclick reset {
        n = 0;
        setnum value n;
        setcolor value #1DB954;
    };

    show;
}

Example: HTTP Fetch

fn main() {
    httpget "https://example.com";

    prints "status ";
    print(netstatus());
    prints " / ";
    print(netlength());
    prints " bytes\n";

    netsave "page.html";
}
status 200 / 559 bytes