No results found for
Raden logo

Raden

A small stack-based scripting language with a C interpreter and native module API. Postfix syntax, first-class functions, and seamless C integration.

# hello.rdn
"hello, world!\n" print

Philosophy

Raden is built around a simple value stack. Everything is a value on the stack — integers, doubles, strings, booleans, nulls, lists, and functions. Operations consume values from the stack and push results back. This keeps the language small, predictable, and easy to embed.

Features

Quick Start

Build

git clone https://github.com/abdorayden/rdn.git
cd rdn
make
./main script.rdn

REPL

Run ./main without arguments to enter the REPL:

$ ./main
raden repl
press Ctrl-D to exit
RDN >> 2 3 + print

Run a script

$ cat hello.rdn
"hello, world!\n" print

$ ./main hello.rdn
hello, world!

Test suite

bash test/run.sh

Command-line flags

./main --version      # print version and exit
./main -v             # same
./main script.rdn     # run script
./main                # start REPL

Values & Types

Raden has six value types. Values are pushed onto the stack, then operations consume them.

TypeSyntaxExample
Integer12342
Double123.4563.14
String"...""hello"
Booleantrue / falsetrue
Nullnullnull
List(...)(1 2 "three")
# values
42
3.14
"hello"
true
null
(1 2 3)

# inspect type
42 type print      # integer
3.14 type print    # double
"hi" type print    # string

String escapes

EscapeMeaning
\nnewline
\ttab
\"double quote
\\backslash
\eescape

Comments

[* this is a comment *]
42 [* inline comment *] print

Operators

Operators consume values from the stack and push results. They are postfix — operands first, operator second.

Arithmetic

OperatorDescriptionExample
+addition2 3 +
-subtraction5 3 -
*multiplication4 5 *
/division10 2 /

Comparison

OperatorDescription
=equal
!=not equal
<less than
>greater than
<=less or equal
>=greater or equal

Boolean

OperatorDescription
!logical NOT
&logical AND (boolean) / bitwise AND (integer)
|logical OR (boolean) / bitwise OR (integer)

Bitwise

OperatorDescription
<<left shift
>>right shift
^bitwise XOR
# arithmetic
2 3 + print      # 5
10 3 - print     # 7
4 5 * print      # 20

# comparison
5 3 > print      # true
"hi" "hi" = print # true

# boolean
true ! print     # false
true false & print # false

Variables

let — bind a value

10 x let
x print           # 10

set — update an existing variable

x 1 + x set
x print           # 11

const — immutable binding

100 limit const
limit print       # 100
limit 5 + const   # error: limit already exists

enum — auto-incrementing constants

enum ONE const    # ONE == 0
enum TWO const    # TWO == 1
enum THREE const  # THREE == 2
reset             # reset back to 0

Built-in environment variables

VariableValue
__host_os"linux", "macos", "windows", etc.
__sharedlib_ext".so", ".dylib", ".dll"
__argvlist of command-line arguments (script path first)

Functions

defun — named functions

double defun
    2 *
end

5 double call print   # 10

apply — it's like functions but used as keyword it called without call keyword

triple apply
    3 *
end

4 triple print   # 12

pcall — protected calls

risky defun
    null 0 index
end

risky pcall
if
    "success: " print print "\n" print
else
    "error: " print print "\n" print     # this runs
end

pcall calls a function and catches any error. On success the stack gets [result, true]. On error it gets [error_string, false] with no stderr output.

call — invoke by name

"double" call call        # looks up function by string name

Function scope

Functions create a new variable scope. Variables defined inside are local unless they already exist in an outer scope (in which case set updates the outer binding).

counter defun
    0 i let               # local i
    i 1 + i set           # update local
    i
end

counter call print        # 1
counter call print        # 1 (each call gets its own scope)

Control Flow

if / else / end

true if
    "yes" print
else
    "no" print
end

loop / end

The loop body must leave a boolean on the stack to continue or stop:

0 i let
i 5 < cond let

cond loop
    i print "\n" print
    i 1 + i set
    i 5 < cond set
    cond
end

break / continue

0 i let
true loop
    i 10 > if break end
    i 5 = if i 1 + i set continue end
    i print "\n" print
    i 1 + i set
    true
end

exit

0 exit       # exit with status 0
1 exit       # exit with status 1

Builtins

Stack

BuiltinSignatureDescription
printvalue ->Print value to stdout
popvalue ->Pop and discard top of stack
dupvalue -> value valueDuplicate top of stack
swapa b -> b aSwap top two values
typevalue -> stringReturn type name as string
to_stringvalue -> stringConvert value to string representation

Variables

letvalue name ->Bind value to name (mutable)
setvalue name ->Update existing variable
constvalue name ->Bind value to name (immutable)
enumname ->Push next enum integer
reset->Reset enum counter

Containers

indextarget index -> valueIndex into list or string
appendtarget value -> targetAppend to list or string (mutates var targets)
removetarget index -> targetRemove item at index (mutates var targets)
lentarget -> integerLength of list or string

Loading

loadpath ->Execute a .rdn file (source-relative)
loadnativepath ->Load a native shared library
add_load_pathpath ->Add a search path for load
add_native_pathpath ->Add a search path for loadnative

Functions

defunname ... end ->Define a named function
applyname ... end ->Define an anonymous function
callname args... -> results...Call a function by name
pcallname args... -> result true|error falseProtected call — catches errors

Control flow

ifcond true-body else? false-body? end ->Conditional execution
loopbody... bool end ->Loop while body leaves true
break->Exit enclosing loop
continue->Skip to next loop iteration
exitstatus ->Exit program with status code

Lists & Strings

Lists

(1 2 3) print            # (1 2 3)

# index
(10 20 30) 1 index print # 20

# append
(1 2) 3 append print     # (1 2 3)

# remove
(1 2 3) 1 remove print   # (1 3)

# length
(1 2 3) len print        # 3

Strings

All sequence builtins work on strings too:

"hello" 1 index print    # "e"
"hello" 1 remove print   # "hllo"
"hi" len print           # 2

# append to string variable (mutates)
"hi" text let
text "!" append
text print               # "hi!"

Nesting

(1 (2 3) 4) 1 index 0 index print   # 2

Error Handling

pcall — protected call

pcall invokes a function and catches any error that occurs during execution. It is the primary error-handling mechanism.

SituationStack after pcall
Success... function_result true
Error caught... "error message" false
div defun
    /      # expects divisor and dividend on stack
end

# protected division
"div" pcall pop
if
    "result: " swap print "\n" print
else
    "error: " print "\n" print
end

Error messages include call traces

When a function calls another function and the inner one fails, the error message includes the full call chain:

inner defun null 0 index end
outer defun inner call end

"outer" pcall pop
if else print "\n" print end
# prints: index requires list or string target
#         while calling function 'inner'
#         while calling function 'outer'

Setup errors are not caught

Errors that happen before the function executes (e.g. missing function name) propagate normally and are not caught by pcall.

Loading Code

load — Raden scripts

# libs/math.rdn
"./libs/math.rdn" load
4 sqrt call print

Paths are resolved relative to the currently running source file.

loadnative — shared libraries

"../nativelibs/files.so" loadnative
"./Makefile" readLines call print

Use __sharedlib_ext for cross-platform paths:

"../nativelibs/math" __sharedlib_ext to_string swap pop append loadnative

Search paths

"./my-libs" add_load_path
"./my-natives" add_native_path

Standard Library — core

libs/core.rdn Small aliases for core builtins and general-purpose helpers.

Operator aliases

FunctionWrapsExample
not!true not call
add, sub, mul, div+ - * /2 3 add call
eq, neq, lt, gt, le, ge= != < > <= >=5 3 gt call
and, or, xor& | ^true false and call
shl, shr<< >>4 1 shl call

Type predicates

is_nullnull is_null call
is_int42 is_int call
is_float3.14 is_float call
is_booltrue is_bool call
is_str"hi" is_str call
is_list(1 2) is_list call

Numeric helpers

inc, dec5 inc call → 6
neg, abs-3 abs call → 3
even, odd4 even call → true
min, max3 7 min call → 3
between5 1 10 between call → true
clamp15 0 10 clamp call → 10

Functional

pipe3 inc pipe call → 4
tapcall fn for side effects, return value
selecttrue "a" "b" select call → "a"
when, unlessconditional transformation
times3 inc times call 0 → 3
range0 5 range call → (0 1 2 3 4)
initerate over list with callback

Standard Library — lists

libs/lists.rdn List-oriented helpers.

FunctionSignatureDescription
each / foreach / maplist callback ->Iterate with side effects
collectlist callback -> listMap — collect callback results
filterlist callback -> listKeep items where callback returns true
reducelist initial callback -> valueFold left
copylist -> listClone list
reverselist -> listReversed clone
concata b -> listConcatenate two lists
first / headlist -> value|nullFirst item
lastlist -> value|nullLast item
tail / restlist -> listList without first item
countlist -> integerLength
isEmptylist -> booleanIs the list empty?
includeslist value -> booleanDoes it contain the value?
anylist callback -> booleanAny item matches?
alllist callback -> booleanAll items match?
shiftvar -> value|nullRemove and return first (mutates var)
popLastvar -> value|nullRemove and return last (mutates var)
unpacklist -> values...Push all items to stack
insertlist index value -> listInsert value at index
# each / collect / filter
(1 2 3) dup collect call print     # (1 2 3)
(1 2 3) 2 > filter call print     # (3)
(1 2 3) 0 add reduce call print   # 6

doubler defun 2 * end
(1 2 3) doubler collect call print # (2 4 6)

Standard Library — strings

libs/strings.rdn String helpers. Also loads the native strings module.

FunctionSignatureDescription
concata b -> stringConcatenate two values as strings
concatBya b sep -> stringJoin with separator
containstext part -> booleanSubstring check
hasPrefixtext prefix -> booleanPrefix check
hasSuffixtext suffix -> booleanSuffix check
trimSpacetext -> stringTrim whitespace
splittext sep -> listSplit by separator
replaceAlltext old new -> stringReplace all occurrences

Standard Library — io

libs/io.rdn Input/output helpers.

FunctionSignatureDescription
readLine / readln-> string|nullRead one line from stdin
readAll-> stringRead remaining stdin
prompttext -> string|nullPrint prompt, then read line
nl->Print newline
println / sayvalue ->Print value then newline
printAlllist ->Print all items
printLineslist ->Print each item on its own line
printJoinlist sep ->Print items joined by separator

Standard Library — files

libs/files.rdn Text file operations.

FunctionSignatureDescription
readLinespath -> listRead file lines
writeLinespath list -> trueWrite lines to file
appendLinespath list -> trueAppend lines to file
readTextpath -> stringRead entire file
writeTextpath text -> trueWrite text to file
appendTextpath text -> trueAppend text to file
slurppath -> stringAlias for readText
spitpath text -> trueAlias for writeText
"./libs/files.rdn" load
"./data.txt" readLines call lines let
lines each call print

Standard Library — math

libs/math.rdn Math constants and functions.

FunctionSignatureDescription
sqrtnumber -> doubleSquare root
powfbase exp -> doublePower
powbase exp -> doublePower (alias)

Constants: M_E, M_LOG2E, M_LOG10E, M_LN2, M_LN10, M_PI, M_PI_2, M_PI_4, M_1_PI, M_2_PI, M_2_SQRTPI, M_SQRT2, M_SQRT1_2

"./libs/math.rdn" load
4 sqrt call print      # 2.0
2 8 pow call print     # 256.0
M_PI print             # 3.14159...

Standard Library — os

libs/os.rdn Operating system runtime helpers.

FunctionSignatureDescription
pid-> integerCurrent process ID
envname -> string|nullEnvironment variable lookup
sleepms -> trueSleep for milliseconds
now-> integerUnix epoch seconds

Standard Library — unix

libs/unix.rdn Filesystem and working-directory helpers.

FunctionSignatureDescription
pwd-> stringCurrent working directory
existspath -> booleanDoes path exist?
mkdirpath -> trueCreate directory
rmPathpath -> trueRemove file or empty directory
lspath -> listList directory entries

Standard Library — path

libs/path.rdn Cross-platform path manipulation.

FunctionSignatureDescription
joinleft right -> stringJoin path components
basenamepath -> stringFinal path component
dirnamepath -> stringParent directory
extnamepath -> string|nullFile extension (no dot)
absolutepath -> booleanIs path absolute?

Standard Library — process

libs/process.rdn External command execution.

FunctionSignatureDescription
statuscommand -> integerRun command, return exit status
outputcommand -> stringRun command, capture stdout

Standard Library — net

libs/net.rdn Networking utilities.

FunctionSignatureDescription
host-> stringLocal hostname
encodeUrltext -> stringPercent-encode a URL
decodeUrltext -> stringPercent-decode a URL

Standard Library — json

libs/json.rdn JSON parsing and serialization.

FunctionSignatureDescription
parsetext -> valueParse JSON — arrays become lists, objects become list of (key value) pairs
stringifyvalue -> textSerialize value to JSON
"./libs/json.rdn" load
"[1, 2, 3]" parse call print   # (1 2 3)
(1 2 3) stringify call print   # [1,2,3]

Standard Library — strconv

libs/strconv.rdn String conversion utilities (Go-inspired).

FunctionSignatureDescription
atoitext -> integerParse string as integer
atoftext -> doubleParse string as float
parseBooltext -> booleanParse "true"/"false"/"1"/"0"
itoainteger -> stringFormat integer as string
formatBoolboolean -> stringFormat boolean as "true"/"false"

Standard Library — flag

libs/flag.rdn Command-line flag parser.

"./libs/flag.rdn" load
"o" "output" true "output file" defun
    output let
    "writing to: " print output print "\n" print
end addFlag call

"v" "verbose" false "verbose output" defun
    "verbose mode\n" print
end addFlag call

execute call
positionals call each call print
FunctionDescription
resetFlagsClear state, reinstall -h/--help
addFlagshort long takesValue description callback → register flag
execute / executeOnRun the parser
positionalsGet positional arguments
programNameGet program name
printHelpPrint usage and flags

Native Module — files

nativelibs/files.c Text file I/O.

FunctionSignature
readLinespath -> list of strings
readTextpath -> string
writeLinespath list -> true
appendLinespath list -> true
writeTextpath text -> true
appendTextpath text -> true

Native Module — io

nativelibs/io.c Stdin reading.

FunctionSignature
readLine-> string or null
readAllInput-> string

Native Module — json

nativelibs/json.c JSON parse/serialize.

FunctionSignature
parseJsontext -> value
stringifyJsonvalue -> text

Native Module — math

nativelibs/math.c Math functions.

FunctionSignature
powerOfbase exponent -> double
sqrtOfnumber -> double

Native Module — net

nativelibs/net.c Network utilities.

FunctionSignature
hostName-> string
urlEncodetext -> string
urlDecodetext -> string

Native Module — path

nativelibs/path.c Path manipulation.

FunctionSignature
joinPathleft right -> string
baseNamepath -> string
dirNamepath -> string
extNamepath -> string or null
isAbsolutePathpath -> boolean

Native Module — process

nativelibs/process.c Command execution.

FunctionSignature
execStatuscommand -> integer
execOutputcommand -> string

Native Module — strconv

nativelibs/strconv.c String conversion.

FunctionSignature
atoitext -> integer
atoftext -> double
parseBooltext -> boolean
itoainteger -> string
formatBoolboolean -> string

Native Module — strings

nativelibs/strings.c String operations.

FunctionSignature
strContainstext part -> boolean
strHasPrefixtext prefix -> boolean
strHasSuffixtext suffix -> boolean
strTrimSpacetext -> string
strSplittext sep -> list
strReplaceAlltext old new -> string

Native Module — syscall

nativelibs/syscall.c System-level helpers.

FunctionSignature
pid-> integer
getEnvname -> string or null
sleepMsms -> true
epochTime-> integer

Native Module — unix

nativelibs/unix.c Filesystem operations.

FunctionSignature
cwd-> string
pathExistspath -> boolean
makeDirpath -> true
removePathpath -> true
listDirpath -> list

Embedding API

Raden exposes a single entry point for embedding:

// embed.c
#include "./include/src.h"

int main(void) {
    char *argv[] = {
        "host",
        "./script.rdn",
    };
    return rdn_main(2, argv);
}

rdn_main

int rdn_main(int argc, char **argv)

The first argument is the program name, the second is the script path. Returns 0 on success, EXIT_FAILURE on error. If argc < 2, it starts the REPL.

Current embedding notes:

  • The public embedding surface is minimal — rdn_main executes a file path
  • Native modules are loaded with loadnative at script runtime
  • Source-relative load and loadnative path resolution works in the embedded context

Writing Native Modules

Native modules are shared libraries that register C functions callable from Raden. The public header is include/rdn_native.h.

Module entry point

Every native module must export:

bool rdn_module_init(RDNModule *module);

Register functions

static bool myFunc(RDNApi *api) {
    // ...
}

bool rdn_module_init(RDNModule *module) {
    return module->register_function(module, "myFunc", myFunc);
}

Stack API

FunctionDescription
api->stack_size(api)Number of items on the stack
api->type(api, index)Type of value at index (-1 = top)
api->to_integer(api, index, &out)Get integer at index
api->to_number(api, index, &out)Get double at index
api->to_boolean(api, index, &out)Get boolean at index
api->to_string(api, index)Get string at index (const char*)
api->pop(api, count)Pop N items
api->push_integer(api, value)Push an integer
api->push_number(api, value)Push a double
api->push_boolean(api, value)Push a boolean
api->push_string(api, value)Push a string (copied)
api->raise_error(api, message)Raise an error (return false)

List API

FunctionDescription
api->push_list(api)Push an empty list
api->list_len(api, index, &len)Get list length
api->list_append(api, list_idx, value_idx)Append (cloned) value to list
api->list_index(api, list_idx, item_idx)Push cloned item from list
api->list_remove(api, list_idx, item_idx)Remove item from list

Full example

// nativelibs/example.c
#include "../include/rdn_native.h"

static bool native_add(RDNApi *api) {
    long left = 0, right = 0;

    if (api->stack_size(api) < 2)
        return api->raise_error(api, "add requires 2 operands");

    if (!api->to_integer(api, -2, &left) ||
        !api->to_integer(api, -1, &right))
        return api->raise_error(api, "add requires integers");

    api->pop(api, 2);
    return api->push_integer(api, left + right);
}

bool rdn_module_init(RDNModule *module) {
    return module->register_function(module, "add", native_add);
}

Build a native module

gcc -Wall -Wextra -Werror -ggdb -std=c11 -fPIC -shared \
    mymodule.c -I. -o mymodule.so

Load a native module

"./mymodule.so" loadnative

# now call registered functions by name
3 4 add call print   # 7

Editor Support

Neovim

The repo includes a Neovim runtime package:

cp -r ./nvim/* ~/.config/nvim/

This provides:

  • File detection (.rdn)
  • Syntax highlighting
  • ftplugin defaults
  • Basic indent rules

For LazyVim / lazy.nvim:

return {
  {
    dir = "/path/to/rdn/nvim",
    name = "raden.nvim",
    ft = "raden",
  },
}

Treesitter

A starter Treesitter grammar scaffold is at treesitter/raden/.

Test Suite

Raden includes 50+ regression tests:

bash test/run.sh

Each test is a .rdn file in test/ with expected output in test/expected/. The test runner compares stdout+stderr against the expected output and verifies the exit code.

To add a new test:

touch test/my_test.rdn              # your test script
touch test/expected/my_test.out     # expected output
echo 0 > test/expected/my_test.status  # expected exit code (optional)
Raden — MIT License — GitHub