Docs

Feather Documentation

This page covers every part of Feather, from basic output and variables to classes, HTTP requests, and building web applications. Use the sidebar to jump to any topic.

24 topics
Getting Started

Installation

Choose your platform below to install Feather. Once installed, the feather command will be available in your terminal.

curl https://codeberg.org/Feather_lang/Feather/raw/branch/dev/install.sh | bash
cd %LOCALAPPDATA%
git clone https://codeberg.org/Feather_lang/Feather
cd Feather
make build
cd Feather\Interpreter
setx PATH "%PATH%;%LOCALAPPDATA%\Feather\Interpreter"

Usage

Run any .feather file using the Feather CLI:

feather <script.feather>
01 - Basics

Comments

Use ^ to write a comment. The entire line is ignored by the interpreter. Comments must be on their own line.

^ This is a comment
^ Comments must be on their own line
02 - Basics

Output

Use OUT to print a value. By default it does not add a newline.

OUT "Hello, World!"

Add &wrap (or &w) at the end to print with a newline:

OUT "Hello" &wrap
OUT "World" &wrap

Use WRAP on its own line to print a blank newline:

OUT "Hello"
WRAP
OUT "World"
03 - Basics

Variables

Variables store text values. Declare and assign them with VARIABLE (shorthand: v).

VARIABLE name = "Alice"
OUT name

You can reassign a variable at any time:

VARIABLE color = "red"
VARIABLE color = "blue"
OUT color

String Interpolation

Embed a variable or expression inside a string using {...}:

VARIABLE name = "Alice"
OUT "Hello, {name}!"

Global Variables

To make variables accessible in every scope, prefix the declaration with GLOBAL:

GLOBAL VARIABLE name = "Alice"
04 - Basics

String Literals

Feather has two kinds of string literals: regular strings and raw strings.

Regular Strings

Written with double or single quotes. Supports {...} interpolation and escape sequences.

VARIABLE item = "apple"
OUT "I have an {item}"
SequenceMeaning
\nNewline
\tTab
\\Backslash

Raw Strings (backticks)

Written using backticks. Raw strings do not support interpolation. Useful for JSON, templates, and literal content.

VARIABLE greeting = "Hello, world!"
OUT `Greeting: {greeting}`
^ Outputs 'Greeting: {greeting}' NOT 'Greeting: Hello, world!'
05 - Basics

String Methods

String methods are accessed with dot notation on any string variable.

Method / PropertyDescription
.length() / .size()Number of characters
.upperUppercase version
.lowerLowercase version
.scrapeTrim surrounding whitespace and control characters
.condenseRemove all whitespace from the string
.find(sub)Returns the index of first occurrence of sub. Returns -1 if not found
06 - Numbers

Counters

Counters are integer variables. Declare them with COUNTER (shorthand: c).

COUNTER score = 0
OUT score

Quickly increment or decrement with + / -:

COUNTER score = 0
+ score
+ score
- score
OUT score
^ Outputs: 1

Global Counters

GLOBAL COUNTER total = 0

Compound Assignment

All assignment shorthands work on both variables and counters:

COUNTER n = 10
n += 5
n -= 2
n *= 3
n /= 2
OUT n
VARIABLE text = "hello"
text += " world"
OUT text
^ Outputs: hello world

Counter Properties

PropertyDescription
.zero?"true" if the counter is 0
.lengthNumber of digits
COUNTER x = 0
OUT x.zero? &wrap
+ x
OUT x.zero? &wrap
07 - Numbers

Operators & Expressions

Feather supports arithmetic, comparison, and logical operators anywhere an expression is expected.

Arithmetic

OperatorDescription
+Addition
-Subtraction
*Multiplication
/Division
%Modulo
COUNTER result = 3 + 4 * 2
OUT result
^ Outputs: 11

COUNTER result = (3 + 4) * 2
OUT result
^ Outputs: 14

Comparison

OperatorDescription
=Equal
!=Not equal
<Less than
>Greater than
<=Less than or equal
>=Greater than or equal

Logical

OperatorDescription
AND / &&Logical AND
OR / ||Logical OR
NOT / !Logical NOT
08 - Input / Output

Taking User Input

Get user input with the .take method. The string it's called on is the prompt shown to the user.

VARIABLE name = "What's your name? ".take
WRAP
COUNTER age = "How old are you? ".take
WRAP
OUT "My name is {name}! I am {age} year(s) old!" &wrap

TAKE requires the user to press Enter. For single-keystroke input without waiting for Enter, use KEYPRESS:

KEYPRESS -> key
OUT "You pressed: {key}" &wrap
09 - Control Flow

Control Flow

Run a block of code only when a condition is true. The body is indented under the IF line.

VARIABLE score = "85"
IF score >= 80:
    OUT "Pass" &wrap

IF / ELSE

VARIABLE score = "55"
IF score >= 60:
    OUT "Pass" &wrap
ELSE:
    OUT "Fail" &wrap

ELSE IF

VARIABLE score = "72"

IF score >= 90:
    OUT "A" &wrap
ELSE IF score >= 80:
    OUT "B" &wrap
ELSE IF score >= 70:
    OUT "C" &wrap
ELSE:
    OUT "D" &wrap

Combining Conditions

VARIABLE age = "25"
VARIABLE member = "true"
IF age >= 18 AND member == "true":
    OUT "Welcome!" &wrap

Using Boolean Properties

COUNTER score = 0
IF score.zero?:
    OUT "You lose!"
10 - Control Flow

Loops

WHILE

Repeats a block as long as a condition is true.

COUNTER i = 0
WHILE i < 5:
    OUT "{i} " &wrap
    i += 1

Use EXIT to break out of a loop early:

COUNTER i = 0
WHILE i < 100:
    IF i = 3:
        EXIT
    OUT "{i} " &wrap
    i += 1

REPEAT

Repeats a block a fixed number of times.

REPEAT 5:
    OUT "hello" &wrap

Track the current iteration count with AS:

REPEAT 3 AS i:
    OUT "Iteration {i}" &wrap
11 - Data Structures

Lists

Lists are ordered collections of values, similar to arrays. Declare them with LIST (shorthand: l).

LIST fruits = ("apple", "banana", "cherry")

^ Multi-line form:
LIST fruits = (
  "apple",
  "banana",
  "cherry"
)

Access items by index (starts at 0), print the whole list, or get its length:

OUT fruits[0] &wrap
OUT fruits[2] &wrap
OUT fruits       ^ print entire list
OUT fruits.length &wrap

Assign to an index:

fruits[1] = "blueberry"
OUT fruits[1] &wrap

Empty List

LIST items

Nested Lists

LIST matrix = (("1", "2"), ("3", "4"))
OUT matrix[0][1] &wrap
^ Outputs: 2

Global Lists

GLOBAL LIST shared = ("a", "b", "c")

FOR Loop

LIST colors = ("red", "green", "blue")
FOR color IN colors:
    OUT color &wrap

Count iterations with AS:

LIST colors = ("red", "green", "blue")
FOR color IN colors AS reps:
    OUT reps &wrap
12 - Data Structures

List Methods

Called with dot notation on any list variable.

Method / PropertyDescription
.length() / .size()Returns number of elements
.empty?Returns "true" if the list has no elements
.append(val)Add val to the end
.pop()Remove and return the last element
.find(val)Returns the index of the first instance of val, or -1
.sort("+")Sort ascending. Use "-" for descending
.reverse()Reverse in place (modifies list, no return)
.join(sep)Join all elements into one string separated by sep
.slice(start, end)Slice from start to end (inclusive), modifies list
.staticParse Feather list to JSON
.contains(query)Returns 'true' or 'false' if the list contains the query
13 - Data Structures

Tables

Tables are key-value stores identical to JSON dicts. Declare them with TABLE (shorthand: t).

TABLE person = {
    "name": "Alice",
    "age": "30",
    "city": "New York"
}

Access values, assign to keys, and get the entry count:

OUT person["name"] &wrap
OUT person["age"] &wrap

person["age"] = "31"
OUT person.length() &wrap

Nested Tables

TABLE config = {
    "server": {
        "host": "localhost",
        "port": "8080"
    },
    "debug": "false"
}

OUT config["server"]["host"] &wrap

Tables with List Values

TABLE data = {
    "colors": ("red", "green", "blue"),
    "count": "3"
}

FOR i IN data["colors"]:
    OUT i &wrap

Global Tables

GLOBAL TABLE settings = {"debug": "false"}

FOREACH loop

TABLE scores = {"Alice": "95", "Bob": "87", "Carol": "92"}
FOREACH name, score IN scores:
    OUT "{name}: {score}" &wrap

Count iterations with AS:

FOREACH name, score IN scores AS reps:
    OUT "{name}: {score} | {reps}" &wrap
14 - Data Structures

Table Methods

All table methods use dot notation.

Method / PropertyDescription
.size() / .length()Number of key-value pairs
.empty?"true" if the table has no entries
.set(key, val)Set or update key to val
.add(key, val)Same as .set, add or update a key
.drop(key)Remove the entry with key
.has(key)Returns "true" if key exists
.keysReturns a list of all keys
.valuesReturns a list of all values
.merge(other)Merge another table in, overwriting duplicate keys
.rename(old, new)Rename a key
.clear()Removes all entries
.copyReturn a shallow copy of the table
.staticSerialize the table to a JSON string
TABLE inventory = {"apples": "10", "bananas": "5"}

OUT inventory.has("bananas") &wrap   ^ true
inventory.drop("bananas")
OUT inventory.size &wrap              ^ 1
inventory.set("oranges", "20")
OUT inventory["oranges"] &wrap        ^ 20

.keys and .values

TABLE prices = {"apple": "1", "banana": "2", "cherry": "3"}
LIST k = prices.keys
LIST v = prices.values

JSON Serialization

^ Table to JSON string:
VARIABLE json = myTable.static

^ JSON string to Table:
TABLE parsed = jsonString.dynamic()
15 - Functions

Functions (Programs)

Define functions with PROGRAM. Call them with CALL. Return a value with RETURN.

Basic Function

PROGRAM greet:
    OUT "Hello from greet!" &wrap
END

CALL greet

Function with Parameters

PROGRAM add(a, b):
    VARIABLE result = a + b
    RETURN result
END

OUT add(10, 5) &wrap
^ Outputs: 15

Inline Call in Expressions

Use funcname(args) directly inside {...} to embed a call inline:

PROGRAM square(x):
    RETURN x * x
END

OUT "Square of 5 is: {square(5)}" &wrap

Returning a List

PROGRAM make_list:
    RETURN ("x", "y", "z")
END

LIST result = make_list()
OUT result &wrap

Returning a Table

PROGRAM make_user(n, a):
    RETURN {"name": n, "age": a}
END

TABLE user = make_user("Bob", "25")
OUT user["name"] &wrap

Scoping

Programs have their own scopes. Variables defined outside are not accessible inside unless declared as GLOBAL:

GLOBAL VARIABLE name = "Alice"

PROGRAM greet():
    OUT "Hi, {name}" &wrap
END

CALL greet
16 - Functions

HTTP Requests

Make HTTP requests with the REQUEST command. The result is stored as a table with response and status keys.

REQUEST myReq {
    "url": "https://api.example.com/users",
    "method": "GET"
}

OUT myReq["status"] &wrap
OUT myReq["response"] &wrap

POST with a JSON Body

TABLE credentials = {
    "username": "alice",
    "password": "secret"
}

VARIABLE body = credentials.static

REQUEST loginReq {
    "url": "https://api.example.com/login",
    "method": "POST",
    "content-type": "application/json",
    "body": body
}

OUT loginReq["status"] &wrap

Parse a JSON Response

REQUEST dataReq {
    "url": "https://api.example.com/data",
    "method": "GET"
}

VARIABLE rawJson = dataReq["response"]
TABLE data = rawJson.dynamic()
OUT data["key"] &wrap

Custom Headers

REQUEST authReq {
    "url": "https://api.example.com/protected",
    "method": "GET",
    "header": "my-token-here"
}

OUT authReq["status"] &wrap
17 - Advanced

Classes

Classes define a reusable blueprint for object-oriented programming. Data stored in classes uses the class's own scope, not the global scope.

CLASS Animal:
    VARIABLE name = "unknown"
    VARIABLE sound = "..."

VARIABLE cat = Animal.new
VARIABLE cat.name = "Cat"
VARIABLE cat.sound = "Meow"
OUT cat.name &wrap
OUT cat.sound &wrap

Methods in Classes

Classes can contain programs that act as methods. Programs inside classes cannot see class-level variables directly, you can use RELATIVE for shared scope:

CLASS Counter:
    RELATIVE COUNTER count = 0
    PROGRAM increment:
        + count
    END
    PROGRAM reset:
        RELATIVE COUNTER count = 0
    END

VARIABLE timer = Counter.new
CALL timer.increment
CALL timer.increment
CALL timer.increment
OUT timer.count &wrap
^ Outputs: 3
CALL timer.reset
OUT timer.count &wrap
^ Outputs: 0
18 - Advanced

Libraries

Import libraries from the libs folder with IMPORT.

IMPORT 'utils.feather'
Note: 'utils' is not part of the standard library, this example assumes you have created a file called utils.feather.
19 - Advanced

Custom Methods

In addition to built-in methods, you can define your own. Methods are similar to programs but attached to a data type. self holds the value being operated on; self.type holds its type name.

Note: Methods do not have their own scope, they use the main scope.
VARIABLE:METHOD method_name:
    ^ ... put code here ...
    RETURN self
END

Accept multiple types by separating them with commas:

VARIABLE, COUNTER:METHOD name:
    ^ ...
    RETURN self.type
END

Methods with Parameters

VARIABLE:METHOD multiply(amount):
    RETURN self * amount
END

OUT `hello`.multiply(3)
^ Outputs: hellohellohello
When calling methods on raw (literal) values, wrap them in backticks, not quotes.

Methods in Imported Files

^ Methods in separate files work normally after import.
^ If the file only contains methods, no alias is needed:
IMPORT 'methods.feather'
20 - System

Using Ruby in Feather

Feather lets you embed and run Ruby code directly. The last expression in the Ruby block is the return value.

RUBY ():
    puts "Hello, world!"
    200
END

Passing Variables and Getting Output

VARIABLE input_example = "Hello, world!"
RUBY (input_example):
    puts input_example
    "Done executing!"
END => output_var

OUT output_var
^ Outputs: Done executing!
Note: Inputs must be variable names, you cannot pass raw values like RUBY ("Hello"). Ruby may take a moment on first run if it needs to initialize a fresh instance.
21 - System

Sending Commands to the System

Run system shell commands with SHELL. Use a raw string (backticks) for the command to avoid symbol conflicts.

SHELL `ls -la` -> output_var
OUT output_var &wrap
Warning: Shell commands have practically no restrictions. Be very careful. When the public online IDE is released, this command will be automatically disabled to prevent damage.
22 - System

Miscellaneous Commands

COLOR

Set the terminal text color. Available colors: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, bold variants (BOLDRED, etc.), and RESET.

COLOR RED
OUT "This is red"
COLOR RESET
OUT "Normal again"

DEBUG

Print each line before it executes, useful for tracing bugs.

DEBUG true
OUT "This line will be traced"
DEBUG false

SAVE

Save the current script state to a file.

SAVE "output.feather"

FORCE_CLOSE

Immediately terminate the interpreter.

FORCE_CLOSE

DISABLE

Disable a command so it does nothing when encountered.

DISABLE $OUT

!IGNOREWARNINGS

Suppress all runtime warnings and errors.

!IGNOREWARNINGS
23 - System

Interacting with Files

Before doing anything with a file, open it with open(file). Files use lists and each index is one line.

Writing to Files

LIST conts = ("line1", "line2")
CALL open('example.txt').write(conts)

^ Write a single line directly:
CALL open('example.txt').write('put content here')
If the file you write to doesn't exist, it will be created automatically.

Reading Files

LIST file_data = open('example.txt').read
OUT file_data

^ Or in-line:
OUT open('example.txt').read

Deleting Files

CALL open('example.txt').delete
24 - Web

Creating Web Applications

Advanced topic. Feather's web server API is similar to Flask, but is powered by Sinatra under the hood.

Defining Routes

Each route is defined with SERVER. You must always specify the allowed HTTP methods.

^ GET only:
SERVER ('/', ('GET')):
    OUT "Got a GET request!" &wrap
    RETURN "Hello from Feather"
END

^ GET and POST:
SERVER ('/', ('GET', 'POST')):
    RETURN "Handled"
END

Request Object

KeyContents
request.methodHTTP method (GET, POST, …)
request.pathRequest path
request.bodyRaw request body
request.headerRequest headers

Starting the Server

SERVER ('/', ('GET')):
    RETURN "Hello"
END

SERVER.start()

Serving External Files

Return a filename string to serve an HTML file directly:

SERVER ('/', ('GET')):
    RETURN 'index.html'
END

SERVER.start()

Multiple Routes

SERVER ('/', ('GET')):
    RETURN 'main'
END

SERVER ('/about/', ('GET')):
    RETURN 'about'
END

SERVER.start()

Full Example

^ app.feather
SERVER ('/', ('GET')):
    RETURN 'index.html'
END

SERVER.start()